summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRob Herring <r.herring@freescale.com>2008-02-14 14:44:21 -0600
committerDaniel Schaeffer <daniel.schaeffer@timesys.com>2008-08-25 15:18:55 -0400
commit3f8ed3afb9cee6648f9650d5daf950bb9347cca6 (patch)
treec9e28cdb34524d44c5dc4dc0778ebdcb858af20c /drivers
parent49914084e797530d9baaf51df9eda77babc98fa8 (diff)
ENGR00065563 Merge L2622-01 to 2.6.24
Merge L2622-01 release to 2.6.24 kernel.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/char/Kconfig51
-rw-r--r--drivers/char/Makefile3
-rw-r--r--drivers/char/mxc_ipc.c2823
-rw-r--r--drivers/char/mxc_mu.c1646
-rw-r--r--drivers/char/mxc_mu_reg.h105
-rw-r--r--drivers/char/mxc_sdma_tty.c744
-rw-r--r--drivers/char/watchdog/mxc_wdt.c385
-rw-r--r--drivers/char/watchdog/mxc_wdt.h37
-rw-r--r--drivers/i2c/busses/Kconfig9
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/mxc_i2c.c762
-rw-r--r--drivers/i2c/busses/mxc_i2c_reg.h40
-rw-r--r--drivers/ide/Kconfig9
-rw-r--r--drivers/ide/arm/Makefile1
-rw-r--r--drivers/ide/arm/mxc_ide.c1122
-rw-r--r--drivers/ide/arm/mxc_ide.h195
-rw-r--r--drivers/input/keyboard/Kconfig7
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/mxc_keyb.c1024
-rw-r--r--drivers/input/keyboard/mxc_keyb.h191
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/mxc_ts.c104
-rw-r--r--drivers/media/video/Kconfig27
-rw-r--r--drivers/media/video/Makefile5
-rw-r--r--drivers/media/video/mxc/capture/Kconfig90
-rw-r--r--drivers/media/video/mxc/capture/Makefile21
-rw-r--r--drivers/media/video/mxc/capture/hv7161.c434
-rw-r--r--drivers/media/video/mxc/capture/hv7161.h38
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_enc.c438
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_sw.h36
-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.c462
-rw-r--r--drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c410
-rw-r--r--drivers/media/video/mxc/capture/ipu_still.c220
-rw-r--r--drivers/media/video/mxc/capture/mc521da.c702
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.c731
-rw-r--r--drivers/media/video/mxc/capture/mt9v111.h421
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.c332
-rw-r--r--drivers/media/video/mxc/capture/mx27_csi.h165
-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/mx27_v4l2_capture.c2077
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.c1865
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.h177
-rw-r--r--drivers/media/video/mxc/capture/s5k3aaex.c572
-rw-r--r--drivers/media/video/mxc/capture/s5k3aaex.h88
-rw-r--r--drivers/media/video/mxc/capture/sensor_clock.c56
-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/Kconfig21
-rw-r--r--drivers/media/video/mxc/output/Makefile8
-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.c1446
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.c1710
-rw-r--r--drivers/media/video/mxc/output/mxc_v4l2_output.h130
-rw-r--r--drivers/mmc/host/Kconfig9
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/mxc_mmc.c1394
-rw-r--r--drivers/mmc/host/mxc_mmc.h128
-rw-r--r--drivers/mtd/chips/cfi_probe.c26
-rw-r--r--drivers/mtd/chips/cfi_util.c11
-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/Kconfig50
-rw-r--r--drivers/mtd/nand/Makefile3
-rw-r--r--drivers/mtd/nand/mxc_nd.c1372
-rw-r--r--drivers/mtd/nand/mxc_nd.h112
-rw-r--r--drivers/mtd/nand/mxc_nd2.c1354
-rw-r--r--drivers/mtd/nand/mxc_nd2.h281
-rw-r--r--drivers/mxc/Kconfig18
-rw-r--r--drivers/mxc/Makefile11
-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.c616
-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/ipu/Kconfig19
-rw-r--r--drivers/mxc/ipu/Makefile5
-rw-r--r--drivers/mxc/ipu/ipu_adc.c677
-rw-r--r--drivers/mxc/ipu/ipu_common.c1804
-rw-r--r--drivers/mxc/ipu/ipu_csi.c217
-rw-r--r--drivers/mxc/ipu/ipu_device.c600
-rw-r--r--drivers/mxc/ipu/ipu_ic.c579
-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.c355
-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.c1000
-rw-r--r--drivers/mxc/pm/Kconfig39
-rw-r--r--drivers/mxc/pm/Makefile12
-rw-r--r--drivers/mxc/pm/dptc.c994
-rw-r--r--drivers/mxc/pm/dptc_mx27.c1416
-rw-r--r--drivers/mxc/pm/dvfs_dptc.c1248
-rw-r--r--drivers/mxc/pm/dvfs_dptc.h83
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx27.h69
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31.h157
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h152
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h158
-rw-r--r--drivers/mxc/pm/dvfs_dptc_table_mxc91321.h68
-rw-r--r--drivers/mxc/pmic/Kconfig62
-rw-r--r--drivers/mxc/pmic/Makefile7
-rw-r--r--drivers/mxc/pmic/core/Makefile15
-rw-r--r--drivers/mxc/pmic/core/mc13783.c368
-rw-r--r--drivers/mxc/pmic/core/pmic-dev.c318
-rw-r--r--drivers/mxc/pmic/core/pmic.h132
-rw-r--r--drivers/mxc/pmic/core/pmic_config.h63
-rw-r--r--drivers/mxc/pmic/core/pmic_core_spi.c326
-rw-r--r--drivers/mxc/pmic/core/pmic_event.c237
-rw-r--r--drivers/mxc/pmic/core/pmic_external.c105
-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.c1504
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_adc_defs.h321
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_audio.c5819
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_battery.c938
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_battery_defs.h81
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_convity.c2483
-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.c3079
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_power_defs.h509
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_rtc.c549
-rw-r--r--drivers/mxc/pmic/mc13783/pmic_rtc_defs.h47
-rw-r--r--drivers/mxc/security/Kconfig101
-rw-r--r--drivers/mxc/security/Makefile21
-rw-r--r--drivers/mxc/security/mxc_hacc.c523
-rw-r--r--drivers/mxc/security/mxc_hacc.h145
-rw-r--r--drivers/mxc/security/mxc_rtic.c838
-rw-r--r--drivers/mxc/security/mxc_rtic.h317
-rw-r--r--drivers/mxc/security/mxc_scc.c2297
-rw-r--r--drivers/mxc/security/mxc_scc_internals.h325
-rw-r--r--drivers/mxc/security/mxc_sec_mod.c67
-rw-r--r--drivers/mxc/security/rng/Makefile29
-rw-r--r--drivers/mxc/security/rng/include/rng_driver.h135
-rw-r--r--drivers/mxc/security/rng/include/rng_internals.h613
-rw-r--r--drivers/mxc/security/rng/include/rng_rnga.h181
-rw-r--r--drivers/mxc/security/rng/include/rng_rngc.h203
-rw-r--r--drivers/mxc/security/rng/include/shw_driver.h2141
-rw-r--r--drivers/mxc/security/rng/include/shw_internals.h158
-rw-r--r--drivers/mxc/security/rng/rng_driver.c1067
-rw-r--r--drivers/mxc/security/rng/shw_driver.c1164
-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.c792
-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_rand.c96
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_sym.c281
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_user.c158
-rw-r--r--drivers/mxc/security/sahara2/fsl_shw_wrap.c732
-rw-r--r--drivers/mxc/security/sahara2/include/adaptor.h95
-rw-r--r--drivers/mxc/security/sahara2/include/diagnostic.h78
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_platform.h115
-rw-r--r--drivers/mxc/security/sahara2/include/fsl_shw.h1964
-rw-r--r--drivers/mxc/security/sahara2/include/linux_port.h1705
-rw-r--r--drivers/mxc/security/sahara2/include/platform_abstractions.h15
-rw-r--r--drivers/mxc/security/sahara2/include/portable_os.h1420
-rw-r--r--drivers/mxc/security/sahara2/include/sah_driver_common.h98
-rw-r--r--drivers/mxc/security/sahara2/include/sah_hardware_interface.h93
-rw-r--r--drivers/mxc/security/sahara2/include/sah_interrupt_handler.h42
-rw-r--r--drivers/mxc/security/sahara2/include/sah_kernel.h89
-rw-r--r--drivers/mxc/security/sahara2/include/sah_memory_mapper.h82
-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.h2184
-rw-r--r--drivers/mxc/security/sahara2/include/sahara2_kernel.h49
-rw-r--r--drivers/mxc/security/sahara2/include/sf_util.h426
-rw-r--r--drivers/mxc/security/sahara2/km_adaptor.c630
-rw-r--r--drivers/mxc/security/sahara2/sah_driver_interface.c1325
-rw-r--r--drivers/mxc/security/sahara2/sah_hardware_interface.c862
-rw-r--r--drivers/mxc/security/sahara2/sah_interrupt_handler.c211
-rw-r--r--drivers/mxc/security/sahara2/sah_memory_mapper.c2041
-rw-r--r--drivers/mxc/security/sahara2/sah_queue.c249
-rw-r--r--drivers/mxc/security/sahara2/sah_queue_manager.c1033
-rw-r--r--drivers/mxc/security/sahara2/sah_status_manager.c684
-rw-r--r--drivers/mxc/security/sahara2/sf_util.c1289
-rw-r--r--drivers/mxc/security/scc2_driver.c2854
-rw-r--r--drivers/mxc/security/scc2_internals.h512
-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.c1238
-rw-r--r--drivers/mxc/ssi/ssi.h574
-rw-r--r--drivers/mxc/ssi/ssi_types.h367
-rw-r--r--drivers/mxc/vpu/Kconfig21
-rw-r--r--drivers/mxc/vpu/Makefile10
-rw-r--r--drivers/mxc/vpu/mxc_vl2cc.c123
-rw-r--r--drivers/mxc/vpu/mxc_vpu.c489
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/cs89x0.c17
-rw-r--r--drivers/net/fec.c570
-rw-r--r--drivers/net/fec.h14
-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
-rw-r--r--drivers/otg/Kconfig52
-rw-r--r--drivers/otg/Kconfig-otg243
-rw-r--r--drivers/otg/Makefile30
-rw-r--r--drivers/otg/functions/acm/Kconfig19
-rw-r--r--drivers/otg/functions/acm/Makefile15
-rw-r--r--drivers/otg/functions/acm/acm-l26.c132
-rw-r--r--drivers/otg/functions/acm/acm.c1478
-rw-r--r--drivers/otg/functions/acm/acm.h510
-rw-r--r--drivers/otg/functions/acm/otg-config.h21
-rw-r--r--drivers/otg/functions/acm/tty-if.c265
-rw-r--r--drivers/otg/functions/acm/tty-l26-os.c2017
-rw-r--r--drivers/otg/functions/acm/tty.h162
-rw-r--r--drivers/otg/functions/generic/Kconfig347
-rw-r--r--drivers/otg/functions/generic/Makefile26
-rw-r--r--drivers/otg/functions/generic/generic-cf.c601
-rw-r--r--drivers/otg/functions/generic/generic-cl.c169
-rw-r--r--drivers/otg/functions/generic/generic.c291
-rw-r--r--drivers/otg/functions/generic/generic.h95
-rw-r--r--drivers/otg/functions/generic/inteltest-cl.c199
-rw-r--r--drivers/otg/functions/generic/otg-config.h133
-rw-r--r--drivers/otg/functions/mouse/Kconfig141
-rw-r--r--drivers/otg/functions/mouse/Makefile26
-rw-r--r--drivers/otg/functions/mouse/mouse-cf.c474
-rw-r--r--drivers/otg/functions/mouse/mouse-fd.c901
-rw-r--r--drivers/otg/functions/mouse/mouse-if.c1032
-rw-r--r--drivers/otg/functions/mouse/otg-config.h143
-rw-r--r--drivers/otg/functions/msc/Kconfig63
-rw-r--r--drivers/otg/functions/msc/Makefile17
-rw-r--r--drivers/otg/functions/msc/crc.c95
-rw-r--r--drivers/otg/functions/msc/crc.h40
-rw-r--r--drivers/otg/functions/msc/msc-bo.c177
-rw-r--r--drivers/otg/functions/msc/msc-fd.c1568
-rw-r--r--drivers/otg/functions/msc/msc-fd.h83
-rw-r--r--drivers/otg/functions/msc/msc-io-l24.c325
-rw-r--r--drivers/otg/functions/msc/msc-io.h72
-rw-r--r--drivers/otg/functions/msc/msc-linux.c1568
-rw-r--r--drivers/otg/functions/msc/msc-scsi.h837
-rw-r--r--drivers/otg/functions/msc/msc.h182
-rw-r--r--drivers/otg/functions/msc/otg-config.h71
-rw-r--r--drivers/otg/functions/msc/trace.c347
-rw-r--r--drivers/otg/functions/network/Kconfig261
-rw-r--r--drivers/otg/functions/network/Makefile25
-rw-r--r--drivers/otg/functions/network/basic-if.c174
-rw-r--r--drivers/otg/functions/network/basic2-if.c185
-rw-r--r--drivers/otg/functions/network/blan-if.c1098
-rw-r--r--drivers/otg/functions/network/ecm-if.c652
-rw-r--r--drivers/otg/functions/network/eem-if.c835
-rw-r--r--drivers/otg/functions/network/fermat.c177
-rw-r--r--drivers/otg/functions/network/fermat.h46
-rw-r--r--drivers/otg/functions/network/net-fd.c906
-rw-r--r--drivers/otg/functions/network/net-ip.c606
-rw-r--r--drivers/otg/functions/network/net-l24-fix.c89
-rw-r--r--drivers/otg/functions/network/net-l24-os.c1682
-rw-r--r--drivers/otg/functions/network/net-os.h141
-rw-r--r--drivers/otg/functions/network/network.h630
-rw-r--r--drivers/otg/functions/network/network_fd.agent83
-rw-r--r--drivers/otg/functions/network/otg-config.h276
-rw-r--r--drivers/otg/functions/network/safe-if.c284
-rw-r--r--drivers/otg/hardware/Kconfig-imx31ads71
-rw-r--r--drivers/otg/hardware/Kconfig-isp130115
-rw-r--r--drivers/otg/hardware/Kconfig-zasevb221
-rw-r--r--drivers/otg/hardware/Kconfig-zasevb-arc51
-rw-r--r--drivers/otg/hardware/Makefile14
-rw-r--r--drivers/otg/hardware/Makefile-imx31ads-l2635
-rw-r--r--drivers/otg/hardware/Makefile-zasevb-arc-l2632
-rw-r--r--drivers/otg/hardware/Makefile-zasevb-l26131
-rw-r--r--drivers/otg/hardware/arc-dev.c291
-rw-r--r--drivers/otg/hardware/arc-hardware.h494
-rw-r--r--drivers/otg/hardware/arc-ocd.c158
-rw-r--r--drivers/otg/hardware/arc-pcd.c1088
-rw-r--r--drivers/otg/hardware/arc-tcd.c260
-rw-r--r--drivers/otg/hardware/arc.h53
-rw-r--r--drivers/otg/hardware/i2c-l26.c295
-rw-r--r--drivers/otg/hardware/imx31ads-l26.c184
-rw-r--r--drivers/otg/hardware/isp1301-hardware.h227
-rw-r--r--drivers/otg/hardware/isp1301-procfs.c468
-rw-r--r--drivers/otg/hardware/isp1301.c1159
-rw-r--r--drivers/otg/hardware/isp1301.h239
-rw-r--r--drivers/otg/hardware/l26-ocd-dev.c118
-rw-r--r--drivers/otg/hardware/l26-ocd.c256
-rw-r--r--drivers/otg/hardware/mxc-gpio.c276
-rw-r--r--drivers/otg/hardware/mxc-gptcr.c286
-rw-r--r--drivers/otg/hardware/mxc-hardware.h816
-rw-r--r--drivers/otg/hardware/mxc-hcd.c1481
-rw-r--r--drivers/otg/hardware/mxc-hcd.h294
-rw-r--r--drivers/otg/hardware/mxc-hrt.c174
-rw-r--r--drivers/otg/hardware/mxc-l26.c1356
-rw-r--r--drivers/otg/hardware/mxc-lnx.h546
-rw-r--r--drivers/otg/hardware/mxc-ocd.c692
-rw-r--r--drivers/otg/hardware/mxc-pcd.c1584
-rw-r--r--drivers/otg/hardware/mxc-pmic.c1111
-rw-r--r--drivers/otg/hardware/mxc-procfs.c772
-rw-r--r--drivers/otg/hardware/mxc91321-gpio.c217
-rw-r--r--drivers/otg/hardware/mxc91331-gpio.c215
-rw-r--r--drivers/otg/hardware/otg-dev.c484
-rw-r--r--drivers/otg/hardware/pcd.c1642
-rw-r--r--drivers/otg/hardware/zasevb-arc-l26.c192
-rw-r--r--drivers/otg/hardware/zasevb-isp1301.c351
-rw-r--r--drivers/otg/hardware/zasevb-l26.c394
-rw-r--r--drivers/otg/hardware/zasevb-mc13783.c317
-rw-r--r--drivers/otg/hardware/zasevb-pmic.c318
-rw-r--r--drivers/otg/otg-config-std.h95
-rw-r--r--drivers/otg/otg/hcd-hw.h114
-rw-r--r--drivers/otg/otg/hcd-l26.h163
-rw-r--r--drivers/otg/otg/hcd-rh.h68
-rw-r--r--drivers/otg/otg/otg-api.h375
-rw-r--r--drivers/otg/otg/otg-compat.h93
-rw-r--r--drivers/otg/otg/otg-dev.h191
-rw-r--r--drivers/otg/otg/otg-fw-df.h68
-rw-r--r--drivers/otg/otg/otg-fw-mn.h352
-rw-r--r--drivers/otg/otg/otg-fw.h1337
-rw-r--r--drivers/otg/otg/otg-hcd.h102
-rw-r--r--drivers/otg/otg/otg-linux.h1384
-rw-r--r--drivers/otg/otg/otg-list.h111
-rw-r--r--drivers/otg/otg/otg-module.h32
-rw-r--r--drivers/otg/otg/otg-ocd.h110
-rw-r--r--drivers/otg/otg/otg-os.h329
-rw-r--r--drivers/otg/otg/otg-pcd.h120
-rw-r--r--drivers/otg/otg/otg-pci.h282
-rw-r--r--drivers/otg/otg/otg-task.h338
-rw-r--r--drivers/otg/otg/otg-tcd.h143
-rw-r--r--drivers/otg/otg/otg-trace.h415
-rw-r--r--drivers/otg/otg/otg-utils.h161
-rw-r--r--drivers/otg/otg/pcd-include.h49
-rw-r--r--drivers/otg/otg/usbp-audio.h357
-rw-r--r--drivers/otg/otg/usbp-bus.h363
-rw-r--r--drivers/otg/otg/usbp-cdc.h689
-rw-r--r--drivers/otg/otg/usbp-chap9.h849
-rw-r--r--drivers/otg/otg/usbp-func.h737
-rw-r--r--drivers/otg/otg/usbp-hid.h98
-rw-r--r--drivers/otg/otg/usbp-hub.h126
-rw-r--r--drivers/otg/otg/usbp-mcpc.h217
-rw-r--r--drivers/otg/otg/usbp-pcd.h296
-rw-r--r--drivers/otg/otgcore/Makefile50
-rw-r--r--drivers/otg/otgcore/core-init-lnx.c313
-rw-r--r--drivers/otg/otgcore/otg-fw-df.c137
-rw-r--r--drivers/otg/otgcore/otg-fw-mn.c928
-rw-r--r--drivers/otg/otgcore/otg-fw.c274
-rw-r--r--drivers/otg/otgcore/otg-linux.c900
-rw-r--r--drivers/otg/otgcore/otg-mesg-lnx.c519
-rw-r--r--drivers/otg/otgcore/otg-mesg.c416
-rw-r--r--drivers/otg/otgcore/otg-trace-lnx.c318
-rw-r--r--drivers/otg/otgcore/otg-trace.c881
-rw-r--r--drivers/otg/otgcore/otg.c1182
-rw-r--r--drivers/otg/otgcore/usbp-bops.c2042
-rw-r--r--drivers/otg/otgcore/usbp-fops.c2385
-rw-r--r--drivers/otg/otgcore/usbp-procfs.c608
-rw-r--r--drivers/pcmcia/Kconfig8
-rw-r--r--drivers/pcmcia/Makefile1
-rw-r--r--drivers/pcmcia/mx31ads-pcmcia.c1295
-rw-r--r--drivers/pcmcia/mx31ads-pcmcia.h157
-rw-r--r--drivers/rtc/Kconfig7
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-mxc.c815
-rw-r--r--drivers/serial/8250.c10
-rw-r--r--drivers/serial/Kconfig25
-rw-r--r--drivers/serial/Makefile2
-rw-r--r--drivers/serial/mxc_uart.c1942
-rw-r--r--drivers/serial/mxc_uart_early.c252
-rw-r--r--drivers/serial/mxc_uart_reg.h128
-rw-r--r--drivers/spi/Kconfig27
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/mxc_spi.c913
-rw-r--r--drivers/spi/mxc_spi.h153
-rw-r--r--drivers/spi/mxc_spi_mx27.h155
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/Makefile3
-rw-r--r--drivers/usb/core/Kconfig7
-rw-r--r--drivers/usb/core/hub.c8
-rw-r--r--drivers/usb/gadget/Kconfig71
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/arcotg_udc.c3126
-rw-r--r--drivers/usb/gadget/arcotg_udc.h598
-rw-r--r--drivers/usb/gadget/ether.c4
-rw-r--r--drivers/usb/gadget/gadget_chips.h8
-rw-r--r--drivers/usb/host/Kconfig47
-rw-r--r--drivers/usb/host/ehci-arc.c393
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci.h6
-rw-r--r--drivers/usb/otg/Kconfig5
-rw-r--r--drivers/usb/otg/Makefile6
-rw-r--r--drivers/usb/otg/fsl_otg.c1078
-rw-r--r--drivers/usb/otg/fsl_otg.h239
-rw-r--r--drivers/usb/otg/otg_fsm.c394
-rw-r--r--drivers/usb/otg/otg_fsm.h152
-rw-r--r--drivers/usb/usblan/Kconfig24
-rw-r--r--drivers/usb/usblan/Makefile8
-rw-r--r--drivers/usb/usblan/usblan-compat.h286
-rw-r--r--drivers/usb/usblan/usblan.c3045
-rw-r--r--drivers/usb/usblan/usblan.h84
-rw-r--r--drivers/video/Kconfig4
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/backlight/Kconfig23
-rw-r--r--drivers/video/backlight/Makefile4
-rw-r--r--drivers/video/backlight/mxc_ipu_bl.c156
-rw-r--r--drivers/video/backlight/mxc_lcdc_bl.c160
-rw-r--r--drivers/video/backlight/mxc_pmic_bl.c197
-rw-r--r--drivers/video/mxc/Kconfig82
-rw-r--r--drivers/video/mxc/Makefile12
-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/mx2fb_epson.c2173
-rw-r--r--drivers/video/mxc/mxcfb.c1476
-rw-r--r--drivers/video/mxc/mxcfb_epson.c1158
-rw-r--r--drivers/video/mxc/mxcfb_epson_qvga.c1146
-rw-r--r--drivers/video/mxc/mxcfb_modedb.c63
-rw-r--r--drivers/video/mxc/mxcfb_sharp_128x128.c1167
-rw-r--r--drivers/video/mxc/mxcfb_toshiba_qvga.c1202
-rw-r--r--drivers/w1/masters/Kconfig6
-rw-r--r--drivers/w1/masters/Makefile2
-rw-r--r--drivers/w1/masters/mxc_w1.c432
-rw-r--r--drivers/w1/slaves/Kconfig17
-rw-r--r--drivers/w1/slaves/Makefile1
-rw-r--r--drivers/w1/slaves/w1_ds2751.c317
-rw-r--r--drivers/w1/w1_family.h1
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
433 files changed, 194545 insertions, 95 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 8cb37e3557d4..ca97a62a0217 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -76,6 +76,8 @@ obj-$(CONFIG_EISA) += eisa/
obj-$(CONFIG_LGUEST_GUEST) += lguest/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_CPU_IDLE) += cpuidle/
+obj-$(CONFIG_ARCH_MXC) += mxc/
+obj-y += otg/
obj-$(CONFIG_MMC) += mmc/
obj-$(CONFIG_NEW_LEDS) += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 2e3a0d4bc4c2..b77901481d37 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -411,6 +411,57 @@ config SGI_MBCS
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
+config MXC_MU
+ bool "MXC Messaging Unit Driver"
+ depends on ARCH_MXC
+ depends on !ARCH_MX3
+ depends on !ARCH_MX33
+ depends on !ARCH_MX27
+ depends on !ARCH_MX21
+ select INPUT
+ default y
+ ---help---
+ This module is called from other modules for
+ message transfers between MCU and DSP core.
+
+config MXC_IPC
+ bool "MXC IPC driver"
+ depends on MXC_MU && MXC_SDMA_API
+ depends on ARCH_MXC
+ depends on !ARCH_MX3
+ depends on !ARCH_MX33
+ depends on !ARCH_MX27
+ depends on !ARCH_MX21
+ default y
+ ---help---
+ This module is called from other modules for
+ message and data transfers between MCU and DSP core.
+
+config MXC_SUPER_GEM
+ bool "MXC Super GEM"
+ depends on MXC_SDMA_API
+ depends on ARCH_MXC
+ depends on !ARCH_MX3
+ depends on !ARCH_MX33
+ depends on !ARCH_MX27
+ depends on !ARCH_MX21
+ default n
+ ---help---
+ Super GEM module called for transfers between MCU and DSP core.
+
+config MXC_SDMA_TTY
+ tristate "MXC SDMA TTY Driver"
+ depends on ARCH_MXC && MXC_SDMA_API
+ depends on !ARCH_MX3
+ depends on !ARCH_MX33
+ depends on !ARCH_MX21
+ depends on !ARCH_MX27
+ select INPUT
+ default y
+ ---help---
+ This module is called from other modules for
+ data transfers between MCU and DSP core.
+
source "drivers/serial/Kconfig"
config UNIX98_PTYS
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 07304d50e0cb..72ead21d2fc5 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -96,6 +96,9 @@ 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_MXC_MU) += mxc_mu.o
+obj-$(CONFIG_MXC_IPC) += mxc_ipc.o
+obj-$(CONFIG_MXC_SDMA_TTY) += mxc_sdma_tty.o
obj-$(CONFIG_MWAVE) += mwave/
obj-$(CONFIG_AGP) += agp/
diff --git a/drivers/char/mxc_ipc.c b/drivers/char/mxc_ipc.c
new file mode 100644
index 000000000000..a89f14c1ec20
--- /dev/null
+++ b/drivers/char/mxc_ipc.c
@@ -0,0 +1,2823 @@
+/*
+ * 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_ipc.c
+ *
+ * @brief This file provides all the kernel level and user level API
+ * definitions for all kind of transfers between MCU and DSP core.
+ *
+ * The Interfaces are with respect to the MCU core. Any driver or user-space
+ * application on the MCU side can transfer messages or data to the DSP side
+ * using the interfaces implemented here.
+ *
+ * @ingroup IPC
+ */
+
+/*
+ * Include Files
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+#include <linux/kthread.h>
+#include <linux/circ_buf.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <linux/dma-mapping.h>
+#include <asm/arch/mxc_mu.h>
+#include <asm/arch/mxc_ipc.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+
+#include "mxc_mu_reg.h"
+
+/*!
+ * Automatic major assignment
+ */
+#define MXC_IPC_MAJOR 0x0
+
+/*! SDMA total receive buffer size, used by kernel space API */
+#define MAX_SDMA_RX_BUF_SIZE 8192
+/*! SDMA transmit buffer maximum size */
+#define MAX_SDMA_TX_BUF_SIZE 8192
+/*!
+ * Number of buffers used for SDMA receive, used by user API. This should
+ * be set to a power of 2.
+ */
+#define NUM_RX_SDMA_BUF 16
+/*! Size of each SDMA receive buffer */
+#define SDMA_RX_BUF_SIZE 512
+/*! MU buffer maximum size */
+#define MAX_MU_BUF_SIZE 512
+
+/*!
+ * Max virtual channels available for this implementation
+ */
+#define IPC_MAX_VIRTUAL_CHANNELS 32
+
+/*!
+ * Max ipc channels available for this implementation
+ */
+#define IPC_MAX_IPC_CHANNEL 10
+
+/*!
+ * This defines an unused virtual channel
+ */
+#define IPC_DUMMY_CHANNEL IPC_MAX_VIRTUAL_CHANNELS + 1
+
+/*!
+ * Max config index allowed for IPC channels. There are three types:
+ * read/write channel for messages
+ * read/write channel for data
+ * read only for data
+ */
+#define IPC_MAX_MU_CHANNEL_INDEX 3
+#define IPC_MAX_SDMA_BIDI_CHANNEL_INDEX 1
+#define IPC_MAX_SDMA_MONO_CHANNEL_INDEX 3
+
+/*!
+ * States allowed for an IPC channel
+ */
+#define CHANNEL_CLOSED 0x0
+#define CHANNEL_OPEN 0x1
+#define CHANNEL_READ_ONGOING 0x2
+#define CHANNEL_WRITE_ONGOING 0x4
+
+/*!
+ * Physical channels used to implement IPC channels.
+ * This defines depends on channels used on DSP side
+ * i.e. if the DSP uses SDMA channel 1 to do writes,
+ * we must use SDMA channel 1 to receive data.
+ *
+ * Currently we use 8 physical channels to implement the
+ * IPC abstraction.
+ *
+ * - MU channel 0 --> IPC channel 0
+ * - MU channel 1 --> IPC channel 1
+ * - MU channel 2 --> IPC channel 2
+ * - MU channel 3 --> IPC channel 3
+ * - SDMA Log channel 0 --> IPC channel 4
+ * - SDMA Log channel 1 --> IPC channel 5
+ * - SDMA Log channel 2 --> IPC channel 6
+ * - SDMA Log channel 3 --> IPC channel 7
+ * - SDMA Data channel 0 --> IPC channel 8
+ * - SDMA Data channel 1 --> IPC channel 9
+ *
+ * Note that IPC channels 8 and 9 uses two physical channels. This
+ * is because SDMA channels are unidirectional and IPC
+ * links must be bidirectional.
+ *
+ */
+#define SHORT_MESSAGE_CHANNEL0 0
+#define SHORT_MESSAGE_CHANNEL1 1
+#define SHORT_MESSAGE_CHANNEL2 2
+#define SHORT_MESSAGE_CHANNEL3 3
+#define LOGGING_CHANNEL0 4
+#define LOGGING_CHANNEL1 5
+#define LOGGING_CHANNEL2 6
+#define LOGGING_CHANNEL3 7
+#define PACKET_DATA_CHANNEL0 8
+#define PACKET_DATA_CHANNEL1 9
+
+/*!
+ * SDMA physical channels used for the different IPC channels
+ */
+#define PACKET_DATA0_SDMA_RD_CHNL 1
+#define PACKET_DATA0_SDMA_WR_CHNL 2
+#define PACKET_DATA1_SDMA_RD_CHNL 3
+#define PACKET_DATA1_SDMA_WR_CHNL 4
+#define LOG0_SDMA_RD_CHNL 5
+#define LOG1_SDMA_RD_CHNL 6
+#define LOG2_SDMA_RD_CHNL 7
+#define LOG3_SDMA_RD_CHNL 8
+
+/*!
+ * Operations allowed on IPC channels. These macros are
+ * used to tell the IPC driver-interface level which
+ * operation to execute.
+*/
+#define IPC_READ 0x1
+#define IPC_WRITE 0x2
+#define IPC_RDWR IPC_READ | IPC_WRITE
+
+#define IPC_WRITE_BYTE_INIT_VAL -4
+
+#define IPC_DEFAULT_MAX_CTRL_STRUCT_NUM 50
+
+typedef void (*read_callback_t) (HW_CTRL_IPC_READ_STATUS_T *);
+typedef void (*write_callback_t) (HW_CTRL_IPC_WRITE_STATUS_T *);
+typedef void (*notify_callback_t) (HW_CTRL_IPC_NOTIFY_STATUS_T *);
+
+/*!
+ * This structure represents the IPC channels SDMA receive buffer.
+ */
+struct sdma_rbuf {
+ /*! Virtual addres of the read buffer */
+ char *buf;
+
+ /*! Physical address of the SDMA read buffers */
+ dma_addr_t rpaddr;
+
+ /*! Amount of data available to read */
+ int count;
+
+ /*! Offset within read buffer */
+ int offset;
+};
+
+/*!
+ * This structure represents the SDMA channels used
+ * by the IPC abstraction.
+ */
+struct sdma_channel {
+ /*! SDMA read channel number */
+ int read_channel;
+
+ /*! SDMA write channel number */
+ int write_channel;
+
+ /*! SDMA write buffer */
+ char *wbuf;
+
+ /*! Physical address of the SDMA write buffer */
+ dma_addr_t wpaddr;
+
+ /*! DMA read buffers, uses muti-buffering scheme */
+ struct sdma_rbuf rbuf[NUM_RX_SDMA_BUF];
+
+ /*! Head to keep track of DMA read buffers */
+ int rbuf_head;
+
+ /*! Tail to keep track of DMA read buffers */
+ int rbuf_tail;
+
+ /*! Head to keep track of DMA write buffers */
+ int wbuf_head;
+
+ /*! Tail to keep track of DMA write buffers */
+ int wbuf_tail;
+
+ /*! Tasklet to call SDMA read callback */
+ struct tasklet_struct read_tasklet;
+
+ /*! Tasklet to call SDMA write callback */
+ struct tasklet_struct write_tasklet;
+};
+
+/*!
+ * This structure represents the MU channels used
+ * by the IPC abstraction.
+ */
+struct mu_channel {
+ /*! MU channel number */
+ int index;
+
+ /*! MU read buffer */
+ struct circ_buf *rbuf;
+
+ /*! MU write buffer */
+ struct circ_buf *wbuf;
+
+ /*! Number of bytes writen */
+ int bytes_written;
+};
+
+/*!
+ * This structure represents an IPC channel. It is represented
+ * by one or even two physical channels, plus a control structure
+ */
+struct ipc_channel {
+ /*! Physical channel(s) used to implement an IPC channel */
+ union {
+ struct sdma_channel sdma;
+ struct mu_channel mu;
+ } ch;
+
+ /*!
+ * Control structure. It handles access and transfers on this
+ * IPC channel
+ */
+ struct ipc_priv_data *priv;
+
+ /*! Lock to protect the channel buffers */
+ spinlock_t channel_lock;
+};
+
+/*!
+ * This structure represents a virtual channel. Multiple virtual channels
+ * could be connected to an IPC channel. IPC abstraction supports up to 32
+ * virtual channels.
+ */
+struct virtual_channel {
+ /*! Controls access to the handle of this channel */
+ struct semaphore sem;
+
+ /*! Current state of the channel */
+ atomic_t state;
+ /*!Added state variables for IPCv2 Read-while -write functionality */
+ atomic_t read_state_ipcv2;
+
+ atomic_t write_state_ipcv2;
+
+ /*!
+ *
+ */
+ char *data;
+
+ /*!
+ * Pointer to the control structure of the IPC channel mapped by
+ * this virtual channel
+ */
+ struct ipc_channel *ipc;
+};
+
+/*!
+ * Private data. Each time an IPC channel is opened, a private
+ * structure is allocated and stored in the priv
+ * field of the file descriptor. This data is
+ * used for further operations on the recently opened
+ * channel
+ */
+struct ipc_priv_data {
+ /*! Blocking mode (non-blocking, blocking) */
+ atomic_t blockmode;
+
+ /*! Physical channel to which the virtual channel is mapped to */
+ unsigned short pchannel;
+
+ /*! Index of virtual channel */
+ unsigned short vchannel;
+
+ /*! This stores number of bytes received */
+ unsigned int read_count;
+
+ /*! This stores number of bytes transmitted */
+ unsigned int write_count;
+
+ /*! Write waitqueue */
+ wait_queue_head_t wq;
+
+ /*! Read waitqueue */
+ wait_queue_head_t rq;
+
+ /*! Store the task_struct structure of the calling process. */
+ struct task_struct *owner;
+
+ /*!
+ * Virtual channel assigned to the IPC channel storing
+ * this structure.
+ */
+ struct virtual_channel *vc;
+
+ /*!
+ * Callbacks used to signal the end of a operation
+ * on an IPC channel. Only for kernel modules.
+ */
+ struct kernel_callbacks *k_callbacks;
+};
+
+/*!
+ * This struct stores the callbacks pointers
+ * used to communicate the end of an operation
+ * on an IPC channel. These callbacks must be
+ * provided by the caller. This feature is available
+ * only for kernel modules.
+ */
+struct kernel_callbacks {
+ read_callback_t read;
+ write_callback_t write;
+ notify_callback_t notify;
+};
+
+/*!
+ * Array used to store the physical channels.
+ */
+static struct ipc_channel ipc_channels[IPC_MAX_IPC_CHANNEL];
+
+/*!
+ * Array used to store the pointers to the virtual channels.
+ * These pointers will be allocated when trying to open a virtual channel.
+ */
+static struct virtual_channel virtual_channels[IPC_MAX_VIRTUAL_CHANNELS];
+
+/*!
+ * Array used to store the channel handlers, correlated to the virtual_channels array.
+ * In future implementations, the handler could have been directly a
+ * virtual_channel pointer, but then it would need an harmonization between AP and BP
+ * structures to properly define what is a handler to a virtual channel on both sides.
+ */
+static HW_CTRL_IPC_CHANNEL_T channel_handlers[IPC_MAX_VIRTUAL_CHANNELS];
+
+/*!
+ * Status registers of the MU channels
+ */
+static unsigned int wbits[4] = { AS_MUMSR_MTE0, AS_MUMSR_MTE1, AS_MUMSR_MTE2,
+ AS_MUMSR_MTE3
+};
+static unsigned int rbits[4] = { AS_MUMSR_MRF0, AS_MUMSR_MRF1, AS_MUMSR_MRF2,
+ AS_MUMSR_MRF3
+};
+
+/*!
+ * Major number
+ */
+static int major_num = 0;
+
+static struct class *mxc_ipc_class;
+
+static void sdma_user_readcb(void *args);
+static void sdma_user_writecb(void *args);
+
+/*!
+ * Locks the virtual channel located on the index channel.
+ * @param channel index of the virtual channel to lock
+ */
+static inline int lock_virtual_channel(unsigned short channel)
+{
+ return (down_interruptible(&virtual_channels[channel].sem));
+}
+
+/*!
+ * Try to locks the virtual channel located on the index channel.
+ * @param channel index of the virtual channel to (try)lock
+ */
+static inline int trylock_virtual_channel(unsigned short channel)
+{
+ return down_trylock(&virtual_channels[channel].sem);
+}
+
+/*!
+ * Unlocks the virtual channel located on the index channel
+ * @param channel index of the virtual channel to unlock
+ */
+static inline void unlock_virtual_channel(unsigned short channel)
+{
+ up(&virtual_channels[channel].sem);
+}
+
+/*!
+ * Finds an unused channel into the virtual channel array
+ *
+ * @return The function returns teh index of the first unused
+ * channel found, IPC_DUMMY_CHANNEL otherwise.
+ */
+inline int get_free_virtual_channel(void)
+{
+ struct virtual_channel *vch = NULL;
+ int i;
+
+ for (i = 0; i < IPC_MAX_VIRTUAL_CHANNELS; i++) {
+ if (trylock_virtual_channel(i) == 1) {
+ continue;
+ }
+
+ vch = &virtual_channels[i];
+
+ if (atomic_read(&vch->state) == CHANNEL_CLOSED) {
+ return i;
+ }
+
+ unlock_virtual_channel(i);
+ }
+
+ return IPC_DUMMY_CHANNEL;
+}
+
+/*!
+ * The MU interrupt handler schedules this tasklet for
+ * execution to start processing a write request.
+ * This function writes data to chnum MU register and signals
+ * the end of the transfer to the servicing thread attached
+ * to the IPC channel.
+ *
+ * @param chnum MU channel number where the operation will occur
+ */
+static void mu_write_tasklet(int chnum)
+{
+ struct mu_channel *mu = NULL;
+ struct ipc_priv_data *priv = NULL;
+ char *p = NULL;
+ int bytes, error = 0;
+
+ priv = ipc_channels[chnum].priv;
+ mu = &ipc_channels[chnum].ch.mu;
+
+ /* Check for bytes in the IPC's MU channel write buffer */
+ bytes = CIRC_CNT(mu->wbuf->head, mu->wbuf->tail, MAX_MU_BUF_SIZE);
+
+ while ((bytes != 0) && (readl(AS_MUMSR) & wbits[chnum])) {
+
+ pr_debug("remaining bytes = %d\n", bytes);
+
+ p = &mu->wbuf->buf[mu->wbuf->tail];
+
+ if (mxc_mu_mcuwrite(mu->index, p)) {
+ pr_debug("Mu MCU write Failed\n");
+ error = 1;
+ break;
+ }
+ mu->wbuf->tail = (mu->wbuf->tail + 4) & (MAX_MU_BUF_SIZE - 1);
+ bytes = CIRC_CNT(mu->wbuf->head, mu->wbuf->tail,
+ MAX_MU_BUF_SIZE);
+ }
+
+ spin_lock(&ipc_channels[chnum].channel_lock);
+ bytes = CIRC_CNT(mu->wbuf->head, mu->wbuf->tail, MAX_MU_BUF_SIZE);
+ /* Disable interrupts if buffer is empty */
+ if ((bytes == 0) && (error == 0)) {
+ pr_debug("(2)remaining bytes = %d\n", bytes);
+ mxc_mu_intdisable(mu->index, TX);
+ }
+ spin_unlock(&ipc_channels[chnum].channel_lock);
+
+ if (waitqueue_active(&priv->wq)) {
+ wake_up_interruptible(&priv->wq);
+ }
+}
+
+/*!
+ * The MU interrupt handler schedules this tasklet for
+ * execution to start processing a read request.
+ * This function reads data from chnum MU register and signals
+ * the end of the transfer to the servicing thread attached
+ * to the IPC channel.
+ *
+ * @param chnum MU channel number where the operation will occur
+ */
+static void mu_read_tasklet(int chnum)
+{
+ struct mu_channel *mu = NULL;
+ struct ipc_priv_data *priv = NULL;
+ int bytes = 0;
+ char p[4];
+ int error = 0;
+
+ priv = ipc_channels[chnum].priv;
+ mu = &ipc_channels[chnum].ch.mu;
+
+ bytes = CIRC_SPACE(mu->rbuf->head, mu->rbuf->tail, MAX_MU_BUF_SIZE);
+
+ while ((bytes >= 4) && (readl(AS_MUMSR) & rbits[chnum])) {
+
+ pr_debug("space available = %d\n", bytes);
+
+ if (mxc_mu_mcuread(mu->index, p)) {
+ pr_debug("MU MCU Read Failed\n");
+ error = 1;
+ break;
+ }
+
+ *((int *)(&(mu->rbuf->buf[mu->rbuf->head]))) = *((int *)p);
+ mu->rbuf->head = (mu->rbuf->head + 4) & (MAX_MU_BUF_SIZE - 1);
+
+ bytes = CIRC_SPACE(mu->rbuf->head, mu->rbuf->tail,
+ MAX_MU_BUF_SIZE);
+ }
+
+ /* Check if space available in read buffer */
+ spin_lock(&ipc_channels[chnum].channel_lock);
+ bytes = CIRC_SPACE(mu->rbuf->head, mu->rbuf->tail, MAX_MU_BUF_SIZE);
+ if ((bytes < 4) && (error == 0)) {
+ mxc_mu_intdisable(mu->index, RX);
+ } else {
+ pr_debug("(2)space available = %d\n", bytes);
+ mxc_mu_intenable(mu->index, RX);
+ }
+ spin_unlock(&ipc_channels[chnum].channel_lock);
+ if (waitqueue_active(&priv->rq)) {
+ wake_up_interruptible(&priv->rq);
+ }
+}
+
+/*!
+ * The MU interrupt handler schedules this tasklet for
+ * execution to start processing a read request.
+ * This function reads data from chnum MU register and signals
+ * the end of the transfer to the servicing thread attached
+ * to the IPC channel.
+ *
+ * This function is used when the caller is a kernel module
+ *
+ * @param chnum MU channel number where the operation will occur
+ */
+static void mu_read_tasklet_kernel(int chnum)
+{
+ struct ipc_priv_data *priv = NULL;
+ struct mu_channel *mu = NULL;
+ char p[4];
+ int bytes = 0;
+ int error = 0;
+
+ priv = ipc_channels[chnum].priv;
+ mu = &ipc_channels[chnum].ch.mu;
+
+ while ((bytes < priv->read_count) && (readl(AS_MUMSR) & rbits[chnum])) {
+ pr_debug("space available = %d\n", bytes);
+ if (mxc_mu_mcuread(mu->index, p)) {
+ pr_debug("MU MCU Read Failed");
+ error = 1;
+ break;
+ }
+
+ *((int *)(&(priv->vc->data[bytes]))) = *((int *)p);
+ bytes += 4;
+ }
+
+ if (error != 0) {
+ mxc_mu_intenable(mu->index, RX);
+ } else {
+ mxc_mu_intdisable(mu->index, RX);
+ mxc_mu_unbind(mu->index, RX);
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+
+ if (priv->k_callbacks->read != NULL) {
+ HW_CTRL_IPC_READ_STATUS_T status;
+
+ status.channel = &channel_handlers[priv->vchannel];
+ status.nb_bytes = bytes;
+ priv->k_callbacks->read(&status);
+ }
+ }
+}
+
+/*!
+ * The MU interrupt handler schedules this tasklet for
+ * execution to start processing a write request.
+ * This function write data to chnum MU register and signals
+ * the end of the transfer to the servicing thread attached
+ * to the IPC channel.
+ *
+ * This function is used when the caller is a kernel module
+ *
+ * @param chnum MU channel number where the operation will occur
+ */
+static void mu_write_tasklet_kernel(int chnum)
+{
+ struct ipc_priv_data *priv = NULL;
+ struct mu_channel *mu = NULL;
+ char *p = NULL;
+ int error = 0;
+
+ priv = ipc_channels[chnum].priv;
+ mu = &ipc_channels[chnum].ch.mu;
+
+ /* Indication that previous write was successful */
+ mu->bytes_written += 4;
+
+ while (mu->bytes_written < priv->write_count) {
+ pr_debug("remaining bytes = %d\n", mu->bytes_written);
+ p = &priv->vc->data[mu->bytes_written];
+ if (mxc_mu_mcuwrite(mu->index, p) != 0) {
+ error = 1;
+ break;
+ }
+ if ((readl(AS_MUMSR) & wbits[chnum]) == 1) {
+ mu->bytes_written += 4;
+ } else {
+ /*
+ * Data not yet read by DSP side, enable interrupts
+ * and exit
+ */
+ break;
+ }
+ }
+
+ /* Enable interrupts if there is data pending to be transmitted */
+ if ((error == 0) && (mu->bytes_written < priv->write_count)) {
+ mxc_mu_intenable(mu->index, TX);
+ } else {
+ mxc_mu_unbind(mu->index, TX);
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+
+ if (priv->k_callbacks->write != NULL) {
+ HW_CTRL_IPC_WRITE_STATUS_T status;
+
+ status.channel = &channel_handlers[priv->vchannel];
+ status.nb_bytes = mu->bytes_written;
+ priv->k_callbacks->write(&status);
+ }
+ }
+}
+
+/*!
+ * Enable MU channel interrupts
+ *
+ * @param chnum MU channel to enable
+ * @return returns 0 on success, negative value otherwise
+ */
+static int mxc_ipc_enable_muints(int chnum)
+{
+ int status = 0;
+ struct mu_channel *mu = NULL;
+
+ mu = &ipc_channels[chnum].ch.mu;
+ /* Bind the default callback functions */
+ status = mxc_mu_bind(mu->index, &mu_read_tasklet, RX);
+ if (status == 0) {
+ status = mxc_mu_bind(mu->index, &mu_write_tasklet, TX);
+ }
+ if (status == 0) {
+ status = mxc_mu_intdisable(mu->index, TX);
+ }
+ /* Enable the RX interrupt to start receiving data */
+ if (status == 0) {
+ status = mxc_mu_intenable(mu->index, RX);
+ }
+
+ /* Cleanup if we had an error at any step */
+ if (status != 0) {
+ pr_debug("Error enabling MU channel %d interrupts \n", chnum);
+ mxc_mu_unbind(mu->index, RX);
+ mxc_mu_unbind(mu->index, TX);
+ }
+
+ return status;
+}
+
+/*!
+ * Allocate a MU channel
+ *
+ * @param chnum MU channel to allocate
+ * @return returns 0 on success, negative value otherwise
+ */
+static int allocate_mu_channel(int chnum)
+{
+ struct mu_channel *mu = NULL;
+ int num = 0, status = 0;
+
+ num = mxc_mu_alloc_channel(chnum);
+ if (num < 0) {
+ pr_debug("Allocation of MU channel %d failed \n", chnum);
+ return num;
+ }
+ mu = &ipc_channels[chnum].ch.mu;
+ mu->index = num;
+
+ status = mxc_mu_intdisable(mu->index, TX);
+ if (status == 0) {
+ status = mxc_mu_intdisable(mu->index, RX);
+ }
+
+ if (status != 0) {
+ mxc_mu_dealloc_channel(mu->index);
+ return status;
+ }
+ /* Allocate the read and write buffers */
+ mu->rbuf = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+ if (mu->rbuf == NULL) {
+ goto err_alloc_rbuf;
+ }
+ mu->rbuf->buf = kmalloc(MAX_MU_BUF_SIZE, GFP_KERNEL);
+ if (mu->rbuf->buf == NULL) {
+ goto err_alloc_rbuf_buf;
+ }
+ mu->wbuf = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+ if (mu->wbuf == NULL) {
+ goto err_alloc_wbuf;
+ }
+ mu->wbuf->buf = kmalloc(MAX_MU_BUF_SIZE, GFP_KERNEL);
+ if (mu->wbuf->buf == NULL) {
+ goto err_alloc_wbuf_buf;
+ }
+ spin_lock_init(&ipc_channels[chnum].channel_lock);
+ mu->wbuf->head = mu->wbuf->tail = 0;
+ mu->rbuf->head = mu->rbuf->tail = 0;
+
+ return 0;
+
+ err_alloc_wbuf_buf:
+ kfree(mu->wbuf);
+ err_alloc_wbuf:
+ kfree(mu->rbuf->buf);
+ err_alloc_rbuf_buf:
+ kfree(mu->rbuf);
+ err_alloc_rbuf:
+ mxc_mu_dealloc_channel(mu->index);
+ return -ENOMEM;
+}
+
+/*! Deallocate a MU channel
+ *
+ * @param chnum MU channel to allocate
+ * @return returns 0 on success, negative value otherwise
+ */
+static int deallocate_mu_channel(int chnum)
+{
+ struct mu_channel *mu = NULL;
+ int ret = 0;
+
+ mu = &ipc_channels[chnum].ch.mu;
+
+ ret = mxc_mu_intdisable(mu->index, TX);
+ ret += mxc_mu_intdisable(mu->index, RX);
+ ret += mxc_mu_unbind(mu->index, TX);
+ ret += mxc_mu_unbind(mu->index, RX);
+ ret += mxc_mu_dealloc_channel(mu->index);
+
+ mu->index = -1;
+
+ /* Free the read and write buffers */
+ kfree(mu->rbuf->buf);
+ kfree(mu->rbuf);
+ kfree(mu->wbuf->buf);
+ kfree(mu->wbuf);
+
+ return ret;
+}
+
+/*!
+ * Start the SDMA read channel accessed from kernel mode
+ *
+ * @param ipc_chnl SDMA channel to start
+ * @param num_od_bd number of BD's allocated for the channel
+ *
+ * @return 0 on succes, negative number if channel does not start
+ */
+static int initialize_sdma_read_channel_kernel(struct ipc_channel *ipc_chnl,
+ int num_of_bd)
+{
+ struct sdma_channel *read_chnl = NULL;
+ dma_channel_params sdma_params;
+ int result = 0;
+
+ read_chnl = &ipc_chnl->ch.sdma;
+
+ memset(&sdma_params, 0, sizeof(dma_channel_params));
+ sdma_params.peripheral_type = DSP;
+ sdma_params.transfer_type = dsp_2_emi;
+ sdma_params.event_id = 0;
+ sdma_params.callback = NULL;
+ sdma_params.arg = 0;
+ sdma_params.bd_number = num_of_bd;
+
+ result = mxc_dma_setup_channel(read_chnl->read_channel, &sdma_params);
+ if (result < 0) {
+ return result;
+ }
+
+ return result;
+}
+
+/*!
+ * Start the SDMA read channel accessed from user mode
+ *
+ * @param ipc_chnl SDMA channel to start
+ *
+ * @return 0 on succes, negative number if channel does not start
+ */
+static int initialize_sdma_read_channel_user(int ipc_chnl)
+{
+ dma_request_t sdma_request;
+ struct ipc_channel ipc_num;
+ struct sdma_channel *read_chnl = NULL;
+ dma_channel_params sdma_params;
+ int result = 0;
+ int i = 0;
+
+ ipc_num = ipc_channels[ipc_chnl];
+ read_chnl = &ipc_channels[ipc_chnl].ch.sdma;
+
+ memset(&sdma_params, 0, sizeof(dma_channel_params));
+ sdma_params.peripheral_type = DSP;
+ sdma_params.transfer_type = dsp_2_emi;
+ sdma_params.event_id = 0;
+ sdma_params.callback = NULL;
+ sdma_params.arg = 0;
+ sdma_params.bd_number = NUM_RX_SDMA_BUF;
+
+ result = mxc_dma_setup_channel(read_chnl->read_channel, &sdma_params);
+ if (result < 0) {
+ return result;
+ } else {
+ for (i = 0; i < NUM_RX_SDMA_BUF; i++) {
+ /* Allocate the DMA'able buffers filled by SDMA engine */
+ read_chnl->rbuf[i].buf =
+ dma_alloc_coherent(NULL, SDMA_RX_BUF_SIZE,
+ &read_chnl->rbuf[i].rpaddr,
+ GFP_DMA);
+ if (read_chnl->rbuf[i].buf == NULL) {
+ result = -1;
+ break;
+ }
+ read_chnl->rbuf[i].count = 0;
+ read_chnl->rbuf[i].offset = 0;
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+ sdma_request.count = SDMA_RX_BUF_SIZE;
+ sdma_request.destAddr =
+ (__u8 *) read_chnl->rbuf[i].rpaddr;
+ sdma_request.bd_cont = 1;
+ result = mxc_dma_set_config(read_chnl->read_channel,
+ &sdma_request, i);
+ if (result != 0) {
+ break;
+ }
+ }
+ }
+
+ if (result != 0) {
+ return result;
+ }
+
+ read_chnl->rbuf_head = 0;
+ read_chnl->rbuf_tail = 0;
+
+ mxc_dma_set_callback(read_chnl->read_channel, sdma_user_readcb,
+ ipc_num.priv);
+ mxc_dma_start(read_chnl->read_channel);
+ return 0;
+}
+
+/*!
+ * Setup the write channel for user mode and kernel mode
+ *
+ * @param ipc_chnl SDMA channel to start
+ * @param kernel_access flag indicates the access type, 1=kernel mode access.
+ * @param num_of_bd number of BD's allocated for the channel
+ *
+ * @return 0 on succes, negative number if channel does not start
+ */
+static int initialize_sdma_write_channel(struct ipc_channel *ipc_chnl,
+ int kernel_access, int num_of_bd)
+{
+ struct sdma_channel *write_chnl = NULL;
+ dma_channel_params sdma_params;
+ int result = 0;
+
+ write_chnl = &ipc_chnl->ch.sdma;
+ memset(&sdma_params, 0, sizeof(dma_channel_params));
+ sdma_params.peripheral_type = DSP;
+ sdma_params.transfer_type = emi_2_dsp;
+ sdma_params.event_id = 0;
+ sdma_params.callback = NULL;
+ sdma_params.arg = 0;
+ sdma_params.bd_number = num_of_bd;
+
+ result = mxc_dma_setup_channel(write_chnl->write_channel, &sdma_params);
+ if (result < 0) {
+ pr_debug("Failed Setting up SDMA channel %d\n",
+ write_chnl->write_channel);
+ mxc_free_dma(write_chnl->write_channel);
+ write_chnl->write_channel = -1;
+ return result;
+ }
+
+ /*
+ * Allocate the write buffer for user space, used to copy user-space
+ * data for DMA.
+ */
+ if (kernel_access == 0) {
+ write_chnl->wbuf =
+ dma_alloc_coherent(NULL, MAX_SDMA_TX_BUF_SIZE,
+ &write_chnl->wpaddr, GFP_DMA);
+ if (write_chnl->wbuf == NULL) {
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * Allocate a SDMA channel
+ *
+ * @param ipc_chnl IPC channel to allocate
+ * @param chnum SDMA channel to allocate
+ * @param mode open mode for this channel (read, write, read/write)
+ *
+ * @return returns 0 on success, negative number otherwise.
+ */
+static int allocate_sdma_channel(int ipc_chnl, int chnum, int mode)
+{
+ struct ipc_channel ipc_num;
+ struct sdma_channel *sdma = NULL;
+ int result = 0;
+
+ ipc_num = ipc_channels[ipc_chnl];
+ sdma = &ipc_channels[ipc_chnl].ch.sdma;
+ if (mode == (IPC_RDWR)) {
+ sdma->write_channel = chnum;
+ sdma->read_channel = chnum - 1;
+ } else {
+ sdma->write_channel = -1;
+ sdma->read_channel = chnum;
+ }
+
+ if (mode & IPC_WRITE) {
+ result =
+ mxc_request_dma(&sdma->write_channel, "IPC WRITE SDMA");
+ if (result < 0) {
+ pr_debug("Failed Opening SDMA channel %d\n",
+ sdma->write_channel);
+ sdma->write_channel = -1;
+ return result;
+ }
+ }
+
+ if (mode & IPC_READ) {
+ result = mxc_request_dma(&sdma->read_channel, "IPC READ SDMA");
+ if (result < 0) {
+ pr_debug("Failed Opening SDMA channel %d\n",
+ sdma->read_channel);
+ sdma->read_channel = -1;
+ if (mode & IPC_WRITE) {
+ mxc_free_dma(sdma->write_channel);
+ sdma->write_channel = -1;
+ }
+ return result;
+ }
+ }
+
+ spin_lock_init(&ipc_channels[chnum].channel_lock);
+ return result;
+}
+
+static void mxc_ipc_free_readbuf_user(int chnum)
+{
+ int i = 0;
+ struct sdma_channel *sdma;
+
+ sdma = &ipc_channels[chnum].ch.sdma;
+
+ /* Free the DMA'able receive buffers */
+ for (i = 0; i < NUM_RX_SDMA_BUF; i++) {
+ if (sdma->rbuf[i].buf != NULL) {
+ dma_free_coherent(NULL, SDMA_RX_BUF_SIZE,
+ sdma->rbuf[i].buf,
+ sdma->rbuf[i].rpaddr);
+ sdma->rbuf[i].buf = NULL;
+ }
+ }
+}
+
+/*!
+ * Deallocate a SDMA channel
+ *
+ * @param chnum SDMA channel to allocate
+ */
+static void deallocate_sdma_channel(int chnum)
+{
+ struct sdma_channel *sdma;
+
+ sdma = &ipc_channels[chnum].ch.sdma;
+ if (sdma->write_channel != -1) {
+ mxc_free_dma(sdma->write_channel);
+ }
+ if (sdma->read_channel != -1) {
+ mxc_free_dma(sdma->read_channel);
+ }
+ sdma->write_channel = -1;
+ sdma->read_channel = -1;
+
+ /* Free the DMA'able transmit buffer */
+ if (sdma->wbuf != NULL) {
+ dma_free_coherent(NULL, MAX_SDMA_TX_BUF_SIZE,
+ sdma->wbuf, sdma->wpaddr);
+ sdma->wbuf = NULL;
+ }
+}
+
+/*!
+ * Copy bytes from the circular buffer to the user buffer
+ *
+ * @param buf buffer to store data
+ * @param count number of bytes to copy
+ * @param cbuf circular_buffer to copy the data from
+ * @param buf_size size of the circular buffer
+ *
+ * @return number of bytes copied into the user buffer
+ */
+static int mxc_ipc_copy_from_mubuf(char *buf, int count, struct circ_buf *cbuf,
+ int buf_size)
+{
+ int n = 0, ret = 0, result = 0;
+
+ while (1) {
+ n = CIRC_CNT_TO_END(cbuf->head, cbuf->tail, buf_size);
+ pr_debug("bytes available in circular buffer = %d\n", n);
+ if (count < n) {
+ n = count;
+ }
+ if (n <= 0) {
+ break;
+ }
+ pr_debug("copying %d bytes into user's buffer\n", n);
+ result = copy_to_user(buf, cbuf->buf + cbuf->tail, n);
+ n -= result;
+ cbuf->tail = (cbuf->tail + n) & (buf_size - 1);
+ buf += n;
+ count -= n;
+ ret += n;
+ /* Check if there is problem copying data */
+ if (result != 0) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*!
+ * Write bytes to the circular buffer from the user buffer
+ *
+ *
+ * @param buf buffer to copy data from
+ * @param count number of bytes to copy
+ * @param cbuf circular_buffer to copy the data to
+ *
+ * @return number of bytes copied into the circular buffer
+ */
+static int mxc_ipc_copy_to_buf(char **buf, int *count, struct circ_buf *cbuf)
+{
+ int n = 0;
+ int result;
+ int ret = 0;
+
+ while (1) {
+ n = CIRC_SPACE_TO_END(cbuf->head, cbuf->tail, MAX_MU_BUF_SIZE);
+ if (*count < n) {
+ n = *count;
+ }
+ if (n < 4) {
+ break;
+ }
+ result = copy_from_user(cbuf->buf + cbuf->head, *buf, n);
+ if (result != 0) {
+ pr_debug("EINVAL error\n");
+ ret = -EFAULT;
+ break;
+ }
+ n -= result;
+ cbuf->head = (cbuf->head + n) & (MAX_MU_BUF_SIZE - 1);
+ *buf += n;
+ *count -= n;
+ }
+
+ return ret;
+}
+
+/*!
+ * This function copies data from the MU buffer into the user read buffer.
+ *
+ * @param priv Private data. Used to synchronize the top half
+ * and the botton half as well as to keep track of
+ * the transfer
+ * @param count number of bytes to be transferred
+ * @param buf buffer where received bytes are saved
+ *
+ * @return number of transferred bytes
+ */
+static int mu_start_dsp_transfer(struct ipc_priv_data *priv,
+ int count, char *buf)
+{
+ int bytes = 0;
+ struct mu_channel *mu = &priv->vc->ipc->ch.mu;
+
+ bytes = mxc_ipc_copy_from_mubuf(buf, count, mu->rbuf, MAX_MU_BUF_SIZE);
+ pr_debug("bytes read = %d\n", bytes);
+
+ spin_lock(&priv->vc->ipc->channel_lock);
+ /* Enable intrs if space in receive buffer */
+ if (CIRC_SPACE(mu->rbuf->head, mu->rbuf->tail, MAX_MU_BUF_SIZE)) {
+ mxc_mu_intenable(mu->index, RX);
+ } else {
+ mxc_mu_intdisable(mu->index, RX);
+ }
+ spin_unlock(&priv->vc->ipc->channel_lock);
+
+ if (bytes == 0) {
+ pr_debug("Could not copy to user buffer error\n");
+ return -EFAULT;
+ }
+
+ return bytes;
+}
+
+/*!
+ * This function starts a copies data into MU buffer and starts the transfer.
+ *
+ * @param priv Private data. Used to synchronize the top half
+ * and the botton half as well as to keep track of
+ * the transfer
+ * @param count number of bytes to be transferred
+ * @param buf buffer containing the bytes to be transferred
+ *
+ * @return number of transferred bytes.
+ */
+static int mu_start_mcu_transfer(struct ipc_priv_data *priv,
+ int count, const char *buf)
+{
+ struct mu_channel *mu = &priv->vc->ipc->ch.mu;
+ int retval = 0, orig_cnt;
+
+ orig_cnt = count;
+
+ /* Copy all the data into write buffer */
+ while (count > 0) {
+ retval = mxc_ipc_copy_to_buf((char **)&buf, &count, mu->wbuf);
+ if (retval < 0) {
+ break;
+ }
+
+ /* Enable TX intr, data available in buffer */
+ spin_lock(&priv->vc->ipc->channel_lock);
+ mxc_mu_intenable(mu->index, TX);
+ spin_unlock(&priv->vc->ipc->channel_lock);
+ /* Check if non-blocking mode requested */
+ if ((atomic_read(&priv->blockmode)) == 0) {
+ break;
+ }
+ if (count == 0) {
+ break;
+ }
+ if (wait_event_interruptible(priv->wq,
+ CIRC_SPACE(mu->wbuf->head,
+ mu->wbuf->tail,
+ MAX_MU_BUF_SIZE))) {
+ break;
+ }
+ }
+
+ pr_debug("bytes written = %d\n", count);
+
+ return orig_cnt - count;
+}
+
+/*!
+ * This function starts a MCU->DSP transfer requested by a kernel module.
+ * This transfer uses a MU channel.
+ *
+ * @param mu the MU channel structure
+ *
+ * @return Zero
+ */
+static int mu_start_mcu_transfer_kernel(struct mu_channel *mu)
+{
+ pr_debug("writing bytes to DSP\n");
+ mxc_mu_bind(mu->index, &mu_write_tasklet_kernel, TX);
+ mxc_mu_intenable(mu->index, TX);
+
+ return 0;
+}
+
+/*!
+ * This function starts a DSP->MCU transfer requested by a kernel module.
+ * This transfer uses a MU channel.
+ *
+ * @param priv Private data. Used to synchronize and to keep track of
+ * the transfer
+ * @param count number of bytes to be transferred
+ * @param buf buffer containing the bytes to be transferred
+ *
+ * @return Zero
+ */
+static int mu_start_dsp_transfer_kernel(struct ipc_priv_data *priv,
+ int count, char *buf)
+{
+ struct mu_channel *mu = &priv->vc->ipc->ch.mu;
+
+ if (count > MAX_MU_BUF_SIZE) {
+ count = MAX_MU_BUF_SIZE;
+ }
+
+ priv->read_count = count;
+ priv->vc->data = buf;
+
+ mxc_mu_bind(mu->index, &mu_read_tasklet_kernel, RX);
+ mxc_mu_intenable(mu->index, RX);
+
+ return 0;
+}
+
+/*!
+ * This function is called by the SDMA's ISR whenever a MCU->DSP
+ * transfer has finished. This callback is used whenever a
+ * kernel module requests a transfer.
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_kernel_writecb(void *args)
+{
+ dma_request_t sdma_request;
+ struct ipc_priv_data *priv = NULL;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ mxc_dma_get_config(sdma->write_channel, &sdma_request, 0);
+
+ priv->write_count = sdma_request.count;
+
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+
+ tasklet_schedule(&sdma->write_tasklet);
+ pr_debug("Called SDMA write callback %d\n", priv->write_count);
+}
+
+/*!
+ * This function is called by the SDMA's ISR whenever a MCU->DSP
+ * transfer has finished. This callback is used whenever a
+ * kernel module requests a transfer.
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_kernel_writecb_ipcv2(void *args)
+{
+ struct ipc_priv_data *priv = NULL;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ priv->write_count = 0;
+
+ atomic_set(&priv->vc->write_state_ipcv2, CHANNEL_OPEN);
+
+ tasklet_schedule(&sdma->write_tasklet);
+ pr_debug("Called SDMA write callback\n");
+}
+
+/*!
+ * This function starts a MCU->DSP transfer requested by a kernel module.
+ * This transfer uses a SDMA channel.
+ *
+ * @param ipc_chn IPC channel data structure
+ * @param count number of bytes to be transferred
+ *
+ * @return Zero
+ */
+static int sdma_start_mcu_transfer_kernel(struct ipc_channel *ipc_chn,
+ int count)
+{
+ dma_request_t sdma_request;
+ struct sdma_channel *sdma = NULL;
+
+ sdma = &ipc_chn->ch.sdma;
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+
+ pr_debug("writing %d bytes to DSP\n", count);
+
+ sdma_request.sourceAddr = (__u8 *) sdma->wpaddr;
+ sdma_request.count = count;
+ sdma_request.bd_cont = 1;
+ mxc_dma_set_config(sdma->write_channel, &sdma_request, sdma->wbuf_head);
+ sdma->wbuf_head++;
+ if (sdma->wbuf_head >= sdma->wbuf_tail) {
+ sdma->wbuf_head = 0;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called by the SDMA's ISR whenever a MCU->DSP
+ * transfer has finished. This callback is used whenever a
+ * user space program requests a transfer.
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_user_writecb(void *args)
+{
+ dma_request_t sdma_request;
+ struct ipc_priv_data *priv = NULL;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ mxc_dma_get_config(sdma->write_channel, &sdma_request, 0);
+
+ pr_debug("Called SDMA write callback %d\n", sdma_request.count);
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+ if (waitqueue_active(&priv->wq)) {
+ wake_up_interruptible(&priv->wq);
+ }
+}
+
+/*!
+ * This function starts a MCU->DSP transfer requested by a user space
+ * program. This transfer uses a SDMA channel.
+ *
+ * @param priv Private data. Useful to synchronize and to keep track of
+ * the transfer
+ * @param count number of bytes to be transferred
+ * @param buf buffer containing the bytes to be transferred
+ *
+ * @return Zero
+ */
+static int sdma_start_mcu_transfer(struct ipc_priv_data *priv,
+ int count, const char *buf)
+{
+ dma_request_t sdma_request;
+ struct sdma_channel *sdma = NULL;
+ int result = 0;
+
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ /* Check if the Channel is in use */
+ while ((atomic_read(&priv->vc->state)) == CHANNEL_WRITE_ONGOING) {
+ pr_debug("Write Block till channel is available\n");
+ if (wait_event_interruptible(priv->wq,
+ (atomic_read(&priv->vc->state) !=
+ CHANNEL_WRITE_ONGOING))) {
+ return -ERESTARTSYS;
+ }
+ }
+
+ result = copy_from_user(sdma->wbuf, buf, count);
+ if (result != 0) {
+ kfree(sdma->wbuf);
+ return -EFAULT;
+ }
+
+ atomic_set(&priv->vc->state, CHANNEL_WRITE_ONGOING);
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+
+ pr_debug("writing %d bytes to SDMA channel %d\n", count,
+ sdma->write_channel);
+
+ sdma_request.sourceAddr = (__u8 *) sdma->wpaddr;
+ sdma_request.count = count;
+
+ mxc_dma_set_callback(sdma->write_channel, sdma_user_writecb, priv);
+ mxc_dma_set_config(sdma->write_channel, &sdma_request, 0);
+ mxc_dma_start(sdma->write_channel);
+
+ return count;
+}
+
+/*!
+ * This function is called by the SDMA ISR whenever a DSP->MCU
+ * transfer has finished. This callback is used whenever a
+ * kernel module requests a transfer.
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_kernel_readcb(void *args)
+{
+ dma_request_t sdma_request;
+ struct ipc_priv_data *priv;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ mxc_dma_get_config(sdma->read_channel, &sdma_request, 0);
+
+ priv->read_count = sdma_request.count;
+
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+
+ tasklet_schedule(&sdma->read_tasklet);
+ pr_debug("Called SDMA read callback %d\n", priv->read_count);
+}
+
+/*!
+ * This function is called by the SDMA ISR whenever a DSP->MCU
+ * transfer has finished. This callback is used whenever a
+ * kernel module requests a transfer.
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_kernel_readcb_ipcv2(void *args)
+{
+ struct ipc_priv_data *priv;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ priv->read_count = 0;
+
+ atomic_set(&priv->vc->read_state_ipcv2, CHANNEL_OPEN);
+
+ tasklet_schedule(&sdma->read_tasklet);
+ pr_debug("Called SDMA read callback\n");
+}
+
+/*!
+ * This function starts a DSP->MCU transfer requested by a kernel module.
+ * This transfer uses a SDMA channel.
+ *
+ * @param priv Private data. Used to synchronize and to keep track of
+ * the transfer
+ * @param count number of bytes to be transferred
+ * @param buf buffer containing the bytes to be transferred
+ *
+ * @return Zero
+ */
+static int sdma_start_dsp_transfer_kernel(struct ipc_priv_data *priv,
+ int count, char *buf)
+{
+ dma_request_t sdma_request;
+ struct sdma_channel *sdma = NULL;
+
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ priv->vc->data = buf;
+
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+
+ pr_debug("writing %d bytes to DSP\n", count);
+
+ sdma_request.destAddr = buf;
+ sdma_request.count = count;
+
+ mxc_dma_set_callback(sdma->read_channel, sdma_kernel_readcb, priv);
+ mxc_dma_set_config(sdma->read_channel, &sdma_request, sdma->rbuf_head);
+ sdma->rbuf_head++;
+ if (sdma->rbuf_head >= sdma->rbuf_tail) {
+ sdma->rbuf_head = 0;
+ }
+ mxc_dma_start(sdma->read_channel);
+
+ return 0;
+}
+
+/*!
+ * This function is called by the SDMA's ISR whenever a DSP->MCU
+ * transfer has finished. This callback fills the SDMA channels read buffer
+ *
+ * @param args points to a priv structure. This structure contains
+ * fields to synchronize the IPC channel and the top half
+ *
+ * @return None
+ */
+static void sdma_user_readcb(void *args)
+{
+ dma_request_t sdma_request;
+ struct ipc_priv_data *priv = NULL;
+ struct sdma_channel *sdma = NULL;
+
+ priv = (struct ipc_priv_data *)args;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+ mxc_dma_get_config(sdma->read_channel, &sdma_request, sdma->rbuf_head);
+
+ while ((sdma_request.bd_done == 0)
+ && (sdma->rbuf[sdma->rbuf_head].count == 0)) {
+ pr_debug("buf head=%d, cnt=%d\n", sdma->rbuf_head,
+ sdma_request.count);
+ sdma->rbuf[sdma->rbuf_head].count = sdma_request.count;
+ sdma->rbuf_head = (sdma->rbuf_head + 1) & (NUM_RX_SDMA_BUF - 1);
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+ mxc_dma_get_config(sdma->read_channel, &sdma_request,
+ sdma->rbuf_head);
+ }
+
+ /* Data arrived in read chnl buffers, wake up any pending processes */
+ if (waitqueue_active(&priv->rq)) {
+ wake_up_interruptible(&priv->rq);
+ }
+}
+
+/*!
+ * Copy bytes from the SDMA read buffers to the user buffer
+ *
+ * @param sdma pointer to the sdma structure
+ * @param buf buffer to store data
+ * @param count number of bytes to copy
+ *
+ * @return number of bytes copied into the user buffer
+ */
+static int mxc_ipc_copy_from_sdmabuf(struct sdma_channel *sdma, char *buf,
+ int count)
+{
+ int n = 0;
+ int amt_cpy = 0;
+ int offset = 0;
+ int ret = 0;
+ char *tmp_buf = NULL;
+ dma_request_t sdma_request;
+ int result;
+
+ while ((sdma->rbuf[sdma->rbuf_tail].count > 0) && (count > 0)) {
+ n = sdma->rbuf[sdma->rbuf_tail].count;
+ offset = sdma->rbuf[sdma->rbuf_tail].offset;
+ tmp_buf = sdma->rbuf[sdma->rbuf_tail].buf + offset;
+
+ /* Use the minimum of the 2 counts to copy data */
+ amt_cpy = (count < n) ? count : n;
+
+ result = copy_to_user(buf, tmp_buf, amt_cpy);
+
+ /* Check if there was problem copying data */
+ if (result != 0) {
+ pr_debug("problem copying into user's buffer\n");
+ break;
+ }
+ pr_debug("copied %d bytes into user's buffer\n", amt_cpy);
+ pr_debug("Buf Tail=%d\n", sdma->rbuf_tail);
+ /* Check if we emptied the SDMA read buffer */
+ if (count < n) {
+ sdma->rbuf[sdma->rbuf_tail].count -= amt_cpy;
+ sdma->rbuf[sdma->rbuf_tail].offset += amt_cpy;
+ } else {
+ sdma->rbuf[sdma->rbuf_tail].count = 0;
+ sdma->rbuf[sdma->rbuf_tail].offset = 0;
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+ sdma_request.count = SDMA_RX_BUF_SIZE;
+ sdma_request.destAddr =
+ (__u8 *) sdma->rbuf[sdma->rbuf_tail].rpaddr;
+ sdma_request.bd_cont = 1;
+ result = mxc_dma_set_config(sdma->read_channel,
+ &sdma_request,
+ sdma->rbuf_tail);
+ sdma->rbuf_tail =
+ (sdma->rbuf_tail + 1) & (NUM_RX_SDMA_BUF - 1);
+
+ /* Restart Sdma channel after data copy */
+
+ mxc_dma_start(sdma->read_channel);
+ }
+ buf += amt_cpy;
+ count -= amt_cpy;
+ ret += amt_cpy;
+ }
+
+ return ret;
+}
+
+/*!
+ * This function allocates a virtual channel.
+ *
+ * @param index index of virtual channel to be open
+ * @param config pointer to a structure containing the type
+ * of channel to open.
+ *
+ * @return 0 on success, negative value otherwise
+ */
+static int allocate_virtual_channel(unsigned short index,
+ const HW_CTRL_IPC_OPEN_T * config)
+{
+ struct kernel_callbacks *k_callbacks;
+ write_callback_t wcallback = NULL;
+ struct ipc_priv_data *priv;
+ unsigned short pchannel;
+ int ret = 0, sdma_chnl = -1;
+
+ priv = (struct ipc_priv_data *)
+ kmalloc(sizeof(struct ipc_priv_data), GFP_KERNEL);
+ if (priv == NULL) {
+ return -1;
+ }
+
+ k_callbacks = (struct kernel_callbacks *)
+ kmalloc(sizeof(struct kernel_callbacks), GFP_KERNEL);
+ if (k_callbacks == NULL) {
+ kfree(priv);
+ return -1;
+ }
+
+ wcallback = config->write_callback;
+ priv->vc = &virtual_channels[index];
+
+ switch (config->type) {
+ case HW_CTRL_IPC_SHORT_MSG:
+ if (config->index > IPC_MAX_MU_CHANNEL_INDEX) {
+ kfree(priv);
+ kfree(k_callbacks);
+ return -1;
+ }
+
+ pchannel = config->index;
+ priv->vc->ipc = &ipc_channels[pchannel];
+ ret = allocate_mu_channel(pchannel);
+ if (ret < 0) {
+ kfree(priv);
+ kfree(k_callbacks);
+ return ret;
+ }
+ break;
+ case HW_CTRL_IPC_PACKET_DATA:
+ if (config->index > IPC_MAX_SDMA_BIDI_CHANNEL_INDEX) {
+ kfree(priv);
+ kfree(k_callbacks);
+ return -1;
+ }
+ if (config->index == 0) {
+ pchannel = PACKET_DATA_CHANNEL0;
+ sdma_chnl = PACKET_DATA0_SDMA_WR_CHNL;
+ } else {
+ pchannel = PACKET_DATA_CHANNEL1;
+ sdma_chnl = PACKET_DATA1_SDMA_WR_CHNL;
+ }
+ priv->vc->ipc = &ipc_channels[pchannel];
+ ret = allocate_sdma_channel(pchannel, sdma_chnl, IPC_RDWR);
+ if (ret == 0) {
+ ret =
+ initialize_sdma_write_channel(priv->vc->ipc, 1,
+ IPC_DEFAULT_MAX_CTRL_STRUCT_NUM);
+ }
+ if (ret == 0) {
+ ret =
+ initialize_sdma_read_channel_kernel(priv->vc->ipc,
+ IPC_DEFAULT_MAX_CTRL_STRUCT_NUM);
+ }
+ if (ret < 0) {
+ deallocate_sdma_channel(pchannel);
+ kfree(priv);
+ kfree(k_callbacks);
+ return ret;
+ }
+ break;
+ case HW_CTRL_IPC_CHANNEL_LOG:
+ if (config->index > IPC_MAX_SDMA_MONO_CHANNEL_INDEX) {
+ kfree(priv);
+ kfree(k_callbacks);
+ return -1;
+ }
+
+ if (config->index == 0) {
+ pchannel = LOGGING_CHANNEL0;
+ sdma_chnl = LOG0_SDMA_RD_CHNL;
+ } else if (config->index == 1) {
+ pchannel = LOGGING_CHANNEL1;
+ sdma_chnl = LOG1_SDMA_RD_CHNL;
+ } else if (config->index == 2) {
+ pchannel = LOGGING_CHANNEL2;
+ sdma_chnl = LOG2_SDMA_RD_CHNL;
+ } else {
+ pchannel = LOGGING_CHANNEL3;
+ sdma_chnl = LOG3_SDMA_RD_CHNL;
+ }
+ priv->vc->ipc = &ipc_channels[pchannel];
+ ret = allocate_sdma_channel(pchannel, sdma_chnl, IPC_READ);
+ if (ret == 0) {
+ ret =
+ initialize_sdma_read_channel_kernel(priv->vc->ipc,
+ IPC_DEFAULT_MAX_CTRL_STRUCT_NUM);
+ }
+ if (ret < 0) {
+ deallocate_sdma_channel(pchannel);
+ kfree(priv);
+ kfree(k_callbacks);
+ return ret;
+ }
+ wcallback = NULL;
+ break;
+ default:
+ kfree(priv);
+ kfree(k_callbacks);
+ return -1;
+ }
+
+ k_callbacks->write = wcallback;
+ k_callbacks->read = config->read_callback;
+ k_callbacks->notify = config->notify_callback;
+
+ /* always non-blocking mode for kernel modules */
+ atomic_set(&priv->blockmode, 0);
+ priv->vchannel = index;
+ priv->pchannel = pchannel;
+ priv->read_count = 0;
+ priv->write_count = 0;
+ priv->vc->ipc->priv = (void *)priv;
+ priv->k_callbacks = k_callbacks;
+
+ return 0;
+}
+
+/*!
+ * Verify that the handler points to the correct handler in the handler array
+ * if not, that mean either the passed handler is NULL, either it is not a correct handler
+ *
+ * @param channel handler to the virtual channel
+ *
+ * @return 1 when handler valid, 0 otherwise
+ */
+
+static int is_channel_handler_valid(HW_CTRL_IPC_CHANNEL_T * channel)
+{
+ if (channel != NULL) {
+ if (channel != &channel_handlers[channel->channel_nb]) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+static void mxc_ipc_sdma_readtasklet(unsigned long arg)
+{
+ struct ipc_priv_data *priv = (struct ipc_priv_data *)arg;
+
+ if (priv->k_callbacks->read != NULL) {
+ HW_CTRL_IPC_READ_STATUS_T status;
+
+ status.channel = &channel_handlers[priv->vchannel];
+ status.nb_bytes = priv->read_count;
+
+ priv->k_callbacks->read(&status);
+ }
+}
+
+static void mxc_ipc_sdma_writetasklet(unsigned long arg)
+{
+ struct ipc_priv_data *priv = (struct ipc_priv_data *)arg;
+
+ if (priv->k_callbacks->write != NULL) {
+ HW_CTRL_IPC_WRITE_STATUS_T status;
+
+ status.channel = &channel_handlers[priv->vchannel];
+ status.nb_bytes = priv->write_count;
+
+ priv->k_callbacks->write(&status);
+ }
+}
+
+/*!
+ * Opens an IPC link. This functions can be called directly by kernel
+ * modules. POSIX implementation of the IPC Driver also calls it.
+ *
+ * @param config Pointer to a struct containing configuration para
+ * meters for the channel to open (type of channel,
+ * callbacks, etc)
+ *
+ * @return returns a virtual channel handler on success, a NULL
+ * pointer otherwise.
+ */
+HW_CTRL_IPC_CHANNEL_T *hw_ctrl_ipc_open(const HW_CTRL_IPC_OPEN_T * config)
+{
+ struct virtual_channel *v;
+ int channel_nb = 0;
+ int status = 0;
+ HW_CTRL_IPC_CHANNEL_T *channel;
+
+ /* return a NULL handler if something goes wrong */
+ channel = NULL;
+
+ if (config == NULL) {
+ return NULL;
+ }
+
+ /* Look for an empty virtual channel, if found, lock it */
+ channel_nb = get_free_virtual_channel();
+
+ /* if free virtual channel not found, return NULL handler */
+ if (channel_nb == IPC_DUMMY_CHANNEL) {
+ return NULL;
+ }
+
+ status = allocate_virtual_channel(channel_nb, config);
+ if (status < 0) {
+ goto cleanup;
+ }
+
+ v = &virtual_channels[channel_nb];
+ atomic_set(&v->state, CHANNEL_OPEN);
+ /*! set state to open for read-while-write IPcv2 functionality */
+ atomic_set(&v->read_state_ipcv2, CHANNEL_OPEN);
+ atomic_set(&v->write_state_ipcv2, CHANNEL_OPEN);
+
+ channel = &channel_handlers[channel_nb];
+
+ /*
+ * set the channel_nb in the handler struct. This allows to
+ * find back the corresponding index
+ * in both channel_handlers and virtual_channels arrays
+ */
+ channel->channel_nb = channel_nb;
+ if (v->ipc->priv->pchannel > 3) {
+ struct sdma_channel *sdma_chnl;
+ sdma_chnl = &v->ipc->ch.sdma;
+
+ sdma_chnl->rbuf_head = 0;
+ sdma_chnl->rbuf_tail = IPC_DEFAULT_MAX_CTRL_STRUCT_NUM;
+ sdma_chnl->wbuf_head = 0;
+ sdma_chnl->wbuf_tail = IPC_DEFAULT_MAX_CTRL_STRUCT_NUM;
+
+ tasklet_init(&sdma_chnl->read_tasklet,
+ mxc_ipc_sdma_readtasklet,
+ (unsigned long)v->ipc->priv);
+
+ tasklet_init(&sdma_chnl->write_tasklet,
+ mxc_ipc_sdma_writetasklet,
+ (unsigned long)v->ipc->priv);
+ }
+
+ pr_debug("Virtual channel %d opened\n", channel_nb);
+
+ cleanup:
+ unlock_virtual_channel(channel_nb);
+
+ return channel;
+}
+
+/*!
+ * Close an IPC link. This functions can be called directly by kernel
+ * modules.
+ *
+ * @param channel handler to the virtual channel to close.
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_close(HW_CTRL_IPC_CHANNEL_T * channel)
+{
+ struct virtual_channel *v;
+ int channel_nb;
+ if (is_channel_handler_valid(channel) == 0) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ channel_nb = channel->channel_nb;
+ v = &virtual_channels[channel_nb];
+ if (atomic_read(&v->state) == CHANNEL_CLOSED ||
+ atomic_read(&v->read_state_ipcv2) == CHANNEL_CLOSED ||
+ atomic_read(&v->write_state_ipcv2) == CHANNEL_CLOSED) {
+ return HW_CTRL_IPC_STATUS_OK;
+ }
+ if (lock_virtual_channel(channel_nb)) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+ kfree(v->ipc->priv->k_callbacks);
+ v->ipc->priv->k_callbacks = NULL;
+
+ if (v->ipc->priv->pchannel > 3) {
+ deallocate_sdma_channel(v->ipc->priv->pchannel);
+ } else {
+ deallocate_mu_channel(v->ipc->priv->pchannel);
+ }
+ kfree(v->ipc->priv);
+ v->ipc->priv = NULL;
+ v->ipc = NULL;
+ unlock_virtual_channel(channel_nb);
+ atomic_set(&v->state, CHANNEL_CLOSED);
+ atomic_set(&v->read_state_ipcv2, CHANNEL_CLOSED);
+ atomic_set(&v->write_state_ipcv2, CHANNEL_CLOSED);
+
+ pr_debug("Virtual channel %d closed\n", channel_nb);
+
+ return HW_CTRL_IPC_STATUS_OK;
+}
+
+/*!
+ * Reads data from an IPC link. This functions can be called directly by kernel
+ * modules. POSIX implementation of the IPC Driver also calls it.
+ *
+ * @param channel handler to the virtual channel where read has been requested
+ * @param buf physical address of DMA'able read buffer to store data read from
+ * the channel.
+ * @param nb_bytes size of the buffer
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_read(HW_CTRL_IPC_CHANNEL_T * channel,
+ unsigned char *buf,
+ unsigned short nb_bytes)
+{
+ struct virtual_channel *v;
+ int status = 0;
+ int channel_nb;
+
+ if (is_channel_handler_valid(channel) == 0) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ if (buf == NULL) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ channel_nb = channel->channel_nb;
+
+ v = &virtual_channels[channel_nb];
+
+ if (lock_virtual_channel(channel_nb)) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ status = atomic_read(&v->state);
+ if (status == CHANNEL_READ_ONGOING) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_READ_ON_GOING;
+ }
+
+ if (status == CHANNEL_CLOSED) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_CHANNEL_UNAVAILABLE;
+ }
+
+ atomic_set(&v->state, CHANNEL_READ_ONGOING);
+
+ v->ipc->priv->read_count = 0;
+ if (v->ipc->priv->pchannel > 3) {
+ struct sdma_channel *sdma_chnl;
+ sdma_chnl = &v->ipc->ch.sdma;
+ status =
+ sdma_start_dsp_transfer_kernel(v->ipc->priv, nb_bytes, buf);
+ } else {
+ status =
+ mu_start_dsp_transfer_kernel(v->ipc->priv, nb_bytes, buf);
+ }
+
+ unlock_virtual_channel(channel_nb);
+ return (status == 0) ? HW_CTRL_IPC_STATUS_OK : HW_CTRL_IPC_STATUS_ERROR;
+}
+
+/*!
+ * Writes data to an IPC link. This functions can be called directly by kernel
+ * modules. POSIX implementation of the IPC Driver also calls it.
+ *
+ * @param channel handler to the virtual channel where read has been requested.
+ * @param buf physical address of DMA'able write buffer containing data to
+ * be written on the channel.
+ * @param nb_bytes size of the buffer
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_write(HW_CTRL_IPC_CHANNEL_T * channel,
+ unsigned char *buf,
+ unsigned short nb_bytes)
+{
+ struct virtual_channel *v;
+ struct sdma_channel *sdma_chnl;
+ struct mu_channel *mu_chnl;
+ int status = 0;
+ int channel_nb;
+
+ if (is_channel_handler_valid(channel) == 0) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ if (buf == NULL) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ channel_nb = channel->channel_nb;
+ v = &virtual_channels[channel_nb];
+
+ if (lock_virtual_channel(channel_nb)) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ status = atomic_read(&v->state);
+
+ if (status == CHANNEL_CLOSED) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_CHANNEL_UNAVAILABLE;
+ }
+
+ if (status == CHANNEL_WRITE_ONGOING) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_WRITE_ON_GOING;
+ }
+
+ atomic_set(&v->state, CHANNEL_WRITE_ONGOING);
+
+ v->ipc->priv->write_count = 0;
+
+ if (v->ipc->priv->pchannel > 3) {
+ sdma_chnl = &v->ipc->ch.sdma;
+ sdma_chnl->wpaddr = (dma_addr_t) buf;
+ status = sdma_start_mcu_transfer_kernel(v->ipc, nb_bytes);
+ mxc_dma_set_callback(sdma_chnl->write_channel,
+ sdma_kernel_writecb, v->ipc->priv);
+ mxc_dma_start(sdma_chnl->write_channel);
+ } else {
+ mu_chnl = &v->ipc->ch.mu;
+ v->ipc->priv->vc->data = buf;
+ mu_chnl->bytes_written = IPC_WRITE_BYTE_INIT_VAL;
+ v->ipc->priv->write_count = nb_bytes;
+ status = mu_start_mcu_transfer_kernel(mu_chnl);
+ }
+
+ pr_debug("transferred %d bytes on virtual channel %d\n", nb_bytes,
+ channel_nb);
+
+ unlock_virtual_channel(channel_nb);
+ return (status == 0) ? HW_CTRL_IPC_STATUS_OK : HW_CTRL_IPC_STATUS_ERROR;
+}
+
+/*!
+ * Writes data to an IPC link. This function can be called directly by kernel
+ * modules. It accepts a linked list or contiguous data.
+ *
+ * @param channel handler to the virtual channel where read has
+ * been requested.
+ * @param mem_ptr pointer of type HW_CTRL_IPC_WRITE_PARAMS_T. Each element
+ * points to the physical address of a DMA'able buffer
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_write_ex(HW_CTRL_IPC_CHANNEL_T * channel,
+ HW_CTRL_IPC_WRITE_PARAMS_T * mem_ptr)
+{
+ struct virtual_channel *v;
+ struct sdma_channel *sdma_chnl = NULL;
+ int status = 0;
+ int channel_nb;
+ HW_CTRL_IPC_LINKED_LIST_T *elt_ptr;
+
+ if (is_channel_handler_valid(channel) == 0) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ channel_nb = channel->channel_nb;
+
+ v = &virtual_channels[channel_nb];
+ if (lock_virtual_channel(channel_nb)) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ status = atomic_read(&v->state);
+ if (status == CHANNEL_CLOSED) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_CHANNEL_UNAVAILABLE;
+ }
+ if (status == CHANNEL_WRITE_ONGOING) {
+ unlock_virtual_channel(channel_nb);
+ return HW_CTRL_IPC_STATUS_WRITE_ON_GOING;
+ }
+ atomic_set(&v->state, CHANNEL_WRITE_ONGOING);
+
+ sdma_chnl = &v->ipc->ch.sdma;
+ v->ipc->priv->write_count = 0;
+
+ if (mem_ptr->ipc_memory_read_mode == HW_CTRL_IPC_MODE_CONTIGUOUS) {
+ sdma_chnl->wpaddr =
+ (dma_addr_t) mem_ptr->read.cont_ptr->data_ptr;
+ status =
+ sdma_start_mcu_transfer_kernel(v->ipc,
+ mem_ptr->read.cont_ptr->
+ length);
+ } else {
+ elt_ptr = mem_ptr->read.list_ptr;
+ while (elt_ptr != NULL) {
+ sdma_chnl->wpaddr = (dma_addr_t) elt_ptr->data_ptr;
+ status =
+ sdma_start_mcu_transfer_kernel(v->ipc,
+ elt_ptr->length);
+ elt_ptr = elt_ptr->next;
+ }
+ }
+ mxc_dma_set_callback(sdma_chnl->write_channel, sdma_kernel_writecb,
+ v->ipc->priv);
+ mxc_dma_start(sdma_chnl->write_channel);
+ pr_debug("transferred bytes on virtual channel %d\n", channel_nb);
+
+ unlock_virtual_channel(channel_nb);
+ return (status == 0) ? HW_CTRL_IPC_STATUS_OK : HW_CTRL_IPC_STATUS_ERROR;
+}
+
+/*!
+ * Used to set various channel parameters
+ *
+ * @param channel handler to the virtual channel where read has
+ * been requested.
+ * @param action IPC driver control action to perform.
+ * @param param parameters required to complete the requested action
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_ioctl(HW_CTRL_IPC_CHANNEL_T * channel,
+ HW_CTRL_IPC_IOCTL_ACTION_T action,
+ void *param)
+{
+ struct virtual_channel *v;
+ struct sdma_channel *sdma_chnl = NULL;
+
+ if (is_channel_handler_valid(channel) == 0) {
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ v = &virtual_channels[channel->channel_nb];
+ switch (action) {
+ case HW_CTRL_IPC_SET_READ_CALLBACK:
+ v->ipc->priv->k_callbacks->read = param;
+ break;
+ case HW_CTRL_IPC_SET_WRITE_CALLBACK:
+ v->ipc->priv->k_callbacks->write = param;
+ break;
+ case HW_CTRL_IPC_SET_NOTIFY_CALLBACK:
+ v->ipc->priv->k_callbacks->notify = param;
+ break;
+ case HW_CTRL_IPC_SET_MAX_CTRL_STRUCT_NB:
+ sdma_chnl = &v->ipc->ch.sdma;
+ initialize_sdma_read_channel_kernel(v->ipc, (int)param);
+ /* Reinitialize the buff pointers to start at zero BD */
+ sdma_chnl->rbuf_tail = (int)param;
+ sdma_chnl->rbuf_head = 0;
+ if (sdma_chnl->write_channel != -1) {
+ initialize_sdma_write_channel(v->ipc, 1, (int)param);
+ sdma_chnl->wbuf_tail = (int)param;
+ sdma_chnl->wbuf_head = 0;
+ }
+ break;
+ default:
+ return HW_CTRL_IPC_STATUS_ERROR;
+ }
+
+ return HW_CTRL_IPC_STATUS_OK;
+}
+
+/*!
+ * This function is a variant on the write() function, and is used to send a
+ * group of frames made of various pieces each to the IPC driver.
+ * It is mandatory to allow high throughput on IPC while minimizing the time
+ * spent in the drivers / interrupts.
+ *
+ * @param channel handler to the virtual channel where read has
+ * been requested.
+ * @param ctrl_ptr Pointer on the control structure.
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_write_ex2(HW_CTRL_IPC_CHANNEL_T * channel,
+ HW_CTRL_IPC_DATA_NODE_DESCRIPTOR_T *
+ ctrl_ptr)
+{
+ int status = 0;
+ struct virtual_channel *v;
+ struct sdma_channel *sdma_chnl = NULL;
+
+ int channel_nb;
+
+ channel_nb = channel->channel_nb;
+
+ v = &virtual_channels[channel_nb];
+
+ status = atomic_read(&v->write_state_ipcv2);
+ if (status == CHANNEL_WRITE_ONGOING) {
+
+ return HW_CTRL_IPC_STATUS_WRITE_ON_GOING;
+ }
+
+ if (status == CHANNEL_CLOSED) {
+
+ return HW_CTRL_IPC_STATUS_CHANNEL_UNAVAILABLE;
+ }
+
+ atomic_set(&v->write_state_ipcv2, CHANNEL_WRITE_ONGOING);
+
+ v = &virtual_channels[channel->channel_nb];
+ sdma_chnl = &v->ipc->ch.sdma;
+ mxc_dma_set_callback(sdma_chnl->write_channel,
+ sdma_kernel_writecb_ipcv2, v->ipc->priv);
+ status = mxc_sdma_write_ipcv2(sdma_chnl->write_channel, ctrl_ptr);
+ return (status == 0) ? HW_CTRL_IPC_STATUS_OK : HW_CTRL_IPC_STATUS_ERROR;
+}
+
+/*!
+ * This function is used to give a set of buffers to the IPC and enable data
+ * transfers.
+ *
+ * @param channel handler to the virtual channel where read has
+ * been requested.
+ * @param ctrl_ptr Pointer on the control structure.
+ *
+ * @return returns HW_CTRL_IPC_STATUS_OK on success, an error code
+ * otherwise.
+ */
+HW_CTRL_IPC_STATUS_T hw_ctrl_ipc_read_ex2(HW_CTRL_IPC_CHANNEL_T * channel,
+ HW_CTRL_IPC_DATA_NODE_DESCRIPTOR_T *
+ ctrl_ptr)
+{
+ int status = 0;
+ struct virtual_channel *v;
+ struct sdma_channel *sdma_chnl = NULL;
+
+ int channel_nb;
+
+ channel_nb = channel->channel_nb;
+
+ v = &virtual_channels[channel_nb];
+
+ status = atomic_read(&v->read_state_ipcv2);
+ if (status == CHANNEL_READ_ONGOING) {
+
+ return HW_CTRL_IPC_STATUS_READ_ON_GOING;
+ }
+
+ if (status == CHANNEL_CLOSED) {
+ return HW_CTRL_IPC_STATUS_CHANNEL_UNAVAILABLE;
+ }
+
+ atomic_set(&v->read_state_ipcv2, CHANNEL_READ_ONGOING);
+ v = &virtual_channels[channel->channel_nb];
+ sdma_chnl = &v->ipc->ch.sdma;
+ mxc_dma_set_callback(sdma_chnl->read_channel, sdma_kernel_readcb_ipcv2,
+ v->ipc->priv);
+ status = mxc_sdma_read_ipcv2(sdma_chnl->read_channel, ctrl_ptr);
+ return (status == 0) ? HW_CTRL_IPC_STATUS_OK : HW_CTRL_IPC_STATUS_ERROR;
+}
+
+/*!
+ * This function is called when an IPC channel is opened. This function does
+ * the initialization of the device corresponding to the channel user want to
+ * open.
+ *
+ * @param inode Pointer to device inode
+ * @param file Pointer to device file structure
+ *
+ * @return The function returns 0 on success, -1 otherwise
+ */
+static int mxc_ipc_open(struct inode *inode, struct file *file)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+ struct ipc_priv_data *priv = NULL;
+ unsigned short vch_index;
+ int ret = 0, mode = IPC_READ, sdma_chnl = -1, ipc_chnl = 0;
+
+ if (((minor == 4) || (minor == 5)) &&
+ (file->f_flags & (O_WRONLY | O_RDWR))) {
+ return -ENXIO;
+ }
+
+ /*look for an empty virtual channel, if found, lock it */
+ vch_index = get_free_virtual_channel();
+ /*if free virtual channel not found, return ENODEV */
+ if (vch_index == IPC_DUMMY_CHANNEL) {
+ return -ENODEV;
+ }
+
+ priv = (struct ipc_priv_data *)
+ kmalloc(sizeof(struct ipc_priv_data), GFP_KERNEL);
+ if (priv == NULL) {
+ unlock_virtual_channel(vch_index);
+ return -ENOMEM;
+ }
+
+ priv->vc = &virtual_channels[vch_index];
+ if (minor < 3) {
+ /* Allocate MU channel */
+ ipc_chnl = minor;
+ priv->vc->ipc = &ipc_channels[ipc_chnl];
+ ret = allocate_mu_channel(ipc_chnl);
+ if (ret < 0) {
+ deallocate_mu_channel(ipc_chnl);
+ kfree(priv);
+ unlock_virtual_channel(vch_index);
+ return ret;
+ }
+ } else {
+ /* Allocate SDMA channel */
+ if (minor == 3) {
+ mode = IPC_RDWR;
+ sdma_chnl = PACKET_DATA1_SDMA_WR_CHNL;
+ ipc_chnl = PACKET_DATA_CHANNEL1;
+ } else if (minor == 4) {
+ mode = IPC_READ;
+ sdma_chnl = LOG0_SDMA_RD_CHNL;
+ ipc_chnl = LOGGING_CHANNEL0;
+ } else if (minor == 5) {
+ mode = IPC_READ;
+ sdma_chnl = LOG1_SDMA_RD_CHNL;
+ ipc_chnl = LOGGING_CHANNEL1;
+ }
+ ret = allocate_sdma_channel(ipc_chnl, sdma_chnl, mode);
+ if (ret < 0) {
+ deallocate_sdma_channel(ipc_chnl);
+ kfree(priv);
+ unlock_virtual_channel(vch_index);
+ return ret;
+ }
+ priv->vc->ipc = &ipc_channels[ipc_chnl];
+ if (mode & IPC_WRITE) {
+ ret =
+ initialize_sdma_write_channel(priv->vc->ipc, 0, 1);
+ if (ret < 0) {
+ deallocate_sdma_channel(ipc_chnl);
+ kfree(priv);
+ unlock_virtual_channel(vch_index);
+ return ret;
+ }
+ }
+ }
+
+ atomic_set(&priv->blockmode, ((file->f_flags & (O_NONBLOCK)) ? 0 : 1));
+ priv->vchannel = vch_index;
+ priv->pchannel = ipc_chnl;
+ priv->owner = current;
+
+ init_waitqueue_head(&priv->wq);
+ init_waitqueue_head(&priv->rq);
+
+ priv->vc->ipc->priv = (void *)priv;
+
+ /* Start the SDMA read channel */
+ if (minor > 2) {
+ ret = initialize_sdma_read_channel_user(ipc_chnl);
+ if (ret < 0) {
+ deallocate_sdma_channel(ipc_chnl);
+ mxc_ipc_free_readbuf_user(ipc_chnl);
+ kfree(priv);
+ unlock_virtual_channel(vch_index);
+ return ret;
+ }
+ } else {
+ /* Start the MU read channel */
+ ret = mxc_ipc_enable_muints(ipc_chnl);
+ if (ret < 0) {
+ deallocate_mu_channel(ipc_chnl);
+ kfree(priv);
+ unlock_virtual_channel(vch_index);
+ return ret;
+ }
+ }
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+
+ file->private_data = (void *)priv;
+
+ unlock_virtual_channel(priv->vchannel);
+
+ pr_debug("Channel successfully opened\n");
+
+ return 0;
+}
+
+/*!
+ * This function is called to close an IPC channel.
+ * Any allocated buffers are freed
+ *
+ * @param inode Pointer to device inode
+ * @param file Pointer to device file structure
+ *
+ * @return The function returns 0 on success or
+ * returns NO_ACCESS if incorrect channel is accessed.
+ */
+static int mxc_ipc_close(struct inode *inode, struct file *file)
+{
+ struct ipc_priv_data *priv = NULL;
+ unsigned int minor;
+ int do_sleep = 0;
+
+ minor = MINOR(file->f_dentry->d_inode->i_rdev);
+ priv = (struct ipc_priv_data *)file->private_data;
+
+ if (lock_virtual_channel(priv->vchannel)) {
+ return -EINTR;
+ }
+
+ if (minor < 3) {
+ struct mu_channel *mu = NULL;
+ mu = &priv->vc->ipc->ch.mu;
+
+ /* Wait to complete any pending writes */
+ /* Block until the write buffer is empty */
+ while (CIRC_CNT(mu->wbuf->head, mu->wbuf->tail, MAX_MU_BUF_SIZE)
+ != 0) {
+ if (wait_event_interruptible
+ (priv->wq,
+ (CIRC_CNT
+ (mu->wbuf->head, mu->wbuf->tail,
+ MAX_MU_BUF_SIZE) == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ deallocate_mu_channel(priv->pchannel);
+ } else {
+ struct sdma_channel *sdma = NULL;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ /* Wait to complete any pending writes */
+ /* Block until the write buffer is empty */
+ while (atomic_read(&priv->vc->state) == CHANNEL_WRITE_ONGOING) {
+ if (wait_event_interruptible(priv->wq,
+ (atomic_read
+ (&priv->vc->state) !=
+ CHANNEL_WRITE_ONGOING))) {
+ return -ERESTARTSYS;
+ }
+ }
+ deallocate_sdma_channel(priv->pchannel);
+ mxc_ipc_free_readbuf_user(priv->pchannel);
+ }
+
+ /* Wake up all waiting processes on the wait queue */
+ while (1) {
+ do_sleep = 0;
+
+ if (waitqueue_active(&priv->wq)) {
+ wake_up(&priv->wq);
+ do_sleep++;
+ }
+ if (waitqueue_active(&priv->rq)) {
+ wake_up(&priv->rq);
+ do_sleep++;
+ }
+ if (!do_sleep) {
+ break;
+ }
+ schedule();
+ }
+
+ priv->vc->ipc = NULL;
+ atomic_set(&priv->vc->state, CHANNEL_CLOSED);
+ priv->vc = NULL;
+
+ unlock_virtual_channel(priv->vchannel);
+
+ kfree(file->private_data);
+ file->private_data = NULL;
+
+ pr_debug("Channel %d closed\n", minor);
+
+ return 0;
+}
+
+/*!
+ * The write function is available to the user-space to perform
+ * a write operation
+ *
+ * @param file Pointer to device file structure
+ * @param buf User buffer, where write data is placed.
+ * @param bytes Size of the requested data transfer
+ * @param off File position where the user is accessing.
+ *
+ * @return Returns number of bytes written or
+ * Returns COUNT_ESIZE if the number of bytes
+ * requested is not a multiple of channel
+ * receive or transmit size or
+ * Returns -EAGAIN for a non-block write when there is
+ * no data in the buffer or
+ * Returns -EFAULT if copy_to_user failed
+ */
+static ssize_t mxc_ipc_write(struct file *file, const char *buf,
+ size_t bytes, loff_t * off)
+{
+ struct ipc_priv_data *priv;
+ unsigned int minor;
+ int count = 0;
+
+ minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+ if ((minor <= 2) && ((bytes % 4) != 0)) {
+ pr_debug("EINVAL error\n");
+ return -EINVAL;
+ }
+
+ if (((minor == 4) || (minor == 5)) &&
+ (file->f_flags & (O_WRONLY | O_RDWR))) {
+ pr_debug("ENXIO error\n");
+ return -ENXIO; /*log channel is read-only */
+ }
+
+ priv = (struct ipc_priv_data *)file->private_data;
+
+ if (lock_virtual_channel(priv->vchannel)) {
+ return -EINTR;
+ }
+
+ pr_debug("bytes to write = %d\n", bytes);
+
+ if (minor > 2) {
+ if (atomic_read(&priv->vc->state) == CHANNEL_WRITE_ONGOING) {
+ /* If non-blocking mode return */
+ if (atomic_read(&priv->blockmode) == 0) {
+ unlock_virtual_channel(priv->vchannel);
+ return -EAGAIN;
+ }
+ }
+ if (bytes > MAX_SDMA_TX_BUF_SIZE) {
+ bytes = MAX_SDMA_TX_BUF_SIZE;
+ }
+ count = sdma_start_mcu_transfer(priv, bytes, buf);
+ } else {
+ count = mu_start_mcu_transfer(priv, bytes, buf);
+ }
+
+ pr_debug("bytes written = %d\n", count);
+
+ unlock_virtual_channel(priv->vchannel);
+
+ return count;
+}
+
+/*!
+ * Check to see if there is data available in the internal buffers
+ *
+ * @param minor minor number used to differentiate between MU and
+ * SDMA buffers
+ * @param priv private data. Useful to synchronize and to keep track of
+ * the transfer
+ * @return returns 0 if no data available or a positive value when data is
+ * available
+ */
+static int mxc_ipc_data_available(int minor, struct ipc_priv_data *priv)
+{
+ int cnt = 0;
+
+ if (minor > 2) {
+ struct sdma_channel *sdma = NULL;
+
+ sdma = &priv->vc->ipc->ch.sdma;
+ cnt = sdma->rbuf[sdma->rbuf_tail].count;
+ pr_debug("bytes in SDMA read buf=%d\n", cnt);
+ } else {
+ struct mu_channel *mu = NULL;
+ mu = &priv->vc->ipc->ch.mu;
+ cnt = CIRC_CNT(mu->rbuf->head, mu->rbuf->tail, MAX_MU_BUF_SIZE);
+ pr_debug("bytes in MU read buf = %d\n", cnt);
+ }
+
+ return cnt;
+}
+
+/*!
+ * The read function is available to the user-space to perform
+ * a read operation
+ *
+ * @param file Pointer to device file structure
+ * @param buf User buffer, where read data to be placed.
+ * @param bytes Size of the requested data transfer
+ * @param off File position where the user is accessing.
+ *
+ * @return Returns number of bytes read or
+ * Returns COUNT_ESIZE if the number of bytes
+ * requested is not a multiple of channel
+ * receive or transmit size or
+ * Returns -EAGAIN for a non-block read when no data
+ * is ready in the buffer or in the register or
+ * Returns -EFAULT if copy_to_user failed
+ */
+static ssize_t mxc_ipc_read(struct file *file, char *buf,
+ size_t bytes, loff_t * off)
+{
+ struct ipc_priv_data *priv;
+ unsigned int minor;
+ int blockmode;
+ int status;
+ int count = 0;
+
+ minor = MINOR(file->f_dentry->d_inode->i_rdev);
+
+ if ((minor <= 2) && ((bytes % 4) != 0)) {
+ return -EINVAL;
+ }
+
+ priv = (struct ipc_priv_data *)file->private_data;
+
+ status = atomic_read(&priv->vc->state);
+ blockmode = atomic_read(&priv->blockmode);
+
+ /*
+ * If non-blocking mode has been requested and no data is available
+ * read channel buffers, return -EAGAIN.
+ */
+ if ((blockmode == 0) && (!mxc_ipc_data_available(minor, priv))) {
+ return -EAGAIN;
+ }
+
+ if (lock_virtual_channel(priv->vchannel)) {
+ return -EINTR;
+ }
+
+ pr_debug("bytes to read = %d\n", bytes);
+
+ /* Block till data is available */
+ while ((mxc_ipc_data_available(minor, priv)) == 0) {
+ pr_debug("No data to read, BLOCK\n");
+ if (wait_event_interruptible(priv->rq,
+ (mxc_ipc_data_available
+ (minor, priv) > 0))) {
+ unlock_virtual_channel(priv->vchannel);
+ return -ERESTARTSYS;
+ }
+ }
+
+ if (minor > 2) {
+ struct sdma_channel *sdma = NULL;
+ sdma = &priv->vc->ipc->ch.sdma;
+
+ count = mxc_ipc_copy_from_sdmabuf(sdma, buf, bytes);
+ } else {
+ count = mu_start_dsp_transfer(priv, bytes, buf);
+ }
+
+ pr_debug("bytes read = %d\n", count);
+
+ atomic_set(&priv->vc->state, CHANNEL_OPEN);
+ unlock_virtual_channel(priv->vchannel);
+
+ return count;
+}
+
+/*!
+ * This function allows the caller to determine whether it can
+ * read from or write to one or more open files without blocking.
+ *
+ * @param filp Pointer to device file structure
+ * @param wait used by poll_wait to indicate a change in the
+ * poll status.
+ *
+ * @return the bit mask describing which operations could
+ * be completed immediately.
+ */
+unsigned int mxc_ipc_poll(struct file *filp, poll_table * wait)
+{
+ struct ipc_priv_data *priv;
+ unsigned int minor;
+ unsigned int mask = 0;
+ int state = 0;
+
+ minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+ priv = (struct ipc_priv_data *)filp->private_data;
+
+ poll_wait(filp, &priv->wq, wait);
+ poll_wait(filp, &priv->rq, wait);
+
+ if (minor > 2) {
+ struct sdma_channel *sdma;
+ sdma = &priv->vc->ipc->ch.sdma;
+ state = atomic_read(&priv->vc->state);
+ /* Check if SDMA Write channel is available */
+ if (state == CHANNEL_OPEN) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ /* Check if data is available in SDMA channel read buffer */
+ if ((mxc_ipc_data_available(minor, priv)) > 0) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ } else {
+ struct mu_channel *mu;
+ mu = &priv->vc->ipc->ch.mu;
+ /* Check for space in MU channel write buffer */
+ if (CIRC_SPACE(mu->wbuf->head, mu->wbuf->tail, MAX_MU_BUF_SIZE)) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
+ /* Check if data is available in MU channel read buffer */
+ if (CIRC_CNT(mu->rbuf->head, mu->rbuf->tail, MAX_MU_BUF_SIZE)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ }
+ pr_debug("Poll mask = %d\n", mask);
+ return mask;
+}
+
+static struct file_operations mxc_ipc_fops = {
+ .owner = THIS_MODULE,
+ .read = mxc_ipc_read,
+ .write = mxc_ipc_write,
+// .writev = mxc_ipc_writev,
+ .poll = mxc_ipc_poll,
+ .open = mxc_ipc_open,
+ .release = mxc_ipc_close,
+};
+
+/*!
+ * This function is used to unload the module.
+ */
+static void ipc_cleanup_module(void)
+{
+ int i;
+ for (i = 0; i <= 5; i++) {
+ class_device_destroy(mxc_ipc_class, MKDEV(major_num, i));
+ }
+ class_destroy(mxc_ipc_class);
+ unregister_chrdev(major_num, "mxc_ipc");
+
+ pr_debug("IPC Driver Module Unloaded\n");
+}
+
+/*!
+ * This function is used to load the module. All initializations and
+ * resources requesting is done here
+ *
+ * @return Returns 0 on success, -1 otherwise
+ */
+int __init ipc_init_module(void)
+{
+ int res = 0;
+ int i;
+ struct class_device *temp_class;
+
+ res = register_chrdev(MXC_IPC_MAJOR, "mxc_ipc", &mxc_ipc_fops);
+ if (res < 0) {
+ pr_debug("IPC Driver Module was not Loaded successfully\n");
+ return res;
+ }
+
+ major_num = res;
+
+ mxc_ipc_class = class_create(THIS_MODULE, "mxc_ipc");
+ if (IS_ERR(mxc_ipc_class)) {
+ printk(KERN_ERR "Error creating mxc_ipc class.\n");
+ unregister_chrdev(major_num, "mxc_ipc");
+ return PTR_ERR(mxc_ipc_class);
+ }
+
+ for (i = 0; i <= 5; i++) {
+ temp_class =
+ class_device_create(mxc_ipc_class, NULL,
+ MKDEV(major_num, i),
+ NULL, "mxc_ipc%u", i);
+
+ if (IS_ERR(temp_class))
+ goto err_out;
+ }
+
+ for (i = 0; i < IPC_MAX_VIRTUAL_CHANNELS; i++) {
+ virtual_channels[i].ipc = NULL;
+ sema_init(&virtual_channels[i].sem, 1);
+ atomic_set(&virtual_channels[i].state, CHANNEL_CLOSED);
+ /* Initialize the channel_handlers array to map the virtual channel indexes */
+ channel_handlers[i].channel_nb = i;
+ }
+
+ printk(KERN_INFO "IPC driver successfully loaded.\n");
+ return major_num;
+
+ err_out:
+ printk(KERN_ERR "Error creating mxc_ipc class device.\n");
+ for (i = 0; i <= 5; i++) {
+ class_device_destroy(mxc_ipc_class, MKDEV(major_num, i));
+ }
+ class_destroy(mxc_ipc_class);
+ unregister_chrdev(major_num, "mxc_ipc");
+ return -1;
+}
+
+module_init(ipc_init_module);
+module_exit(ipc_cleanup_module);
+EXPORT_SYMBOL(hw_ctrl_ipc_open);
+EXPORT_SYMBOL(hw_ctrl_ipc_close);
+EXPORT_SYMBOL(hw_ctrl_ipc_read);
+EXPORT_SYMBOL(hw_ctrl_ipc_write);
+EXPORT_SYMBOL(hw_ctrl_ipc_write_ex);
+EXPORT_SYMBOL(hw_ctrl_ipc_write_ex2);
+EXPORT_SYMBOL(hw_ctrl_ipc_read_ex2);
+EXPORT_SYMBOL(hw_ctrl_ipc_ioctl);
diff --git a/drivers/char/mxc_mu.c b/drivers/char/mxc_mu.c
new file mode 100644
index 000000000000..5d16f62a6246
--- /dev/null
+++ b/drivers/char/mxc_mu.c
@@ -0,0 +1,1646 @@
+/*
+ * 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_mu.c
+ *
+ * @brief This file provides all the kernel level and user level API
+ * definitions for the message transfer between MCU and DSP core.
+ *
+ * The Interfaces are with respect to the MCU core. Any driver on the MCU side
+ * can transfer messages to the DSP side using the interfaces implemented here.
+ *
+ * @ingroup MU
+ */
+
+/*
+ * Include Files
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/circ_buf.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/clk.h>
+#include <asm/arch/mxc_mu.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "mxc_mu_reg.h"
+
+#define DVR_VER "2.0"
+static struct class *mxc_mu_class;
+static int mu_major;
+
+static struct clk *mu_clkp;
+
+/*
+ * Used to calculate number of bytes in the read buffer
+ */
+#define GET_READBYTES(a) CIRC_CNT(mu_rbuf[a].cbuf.head, \
+ mu_rbuf[a].cbuf.tail, MAX_BUF_SIZE)
+
+/*
+ * Used to calculate number of bytes in the write buffer
+ */
+#define GET_WRITEBYTES(a) CIRC_CNT(mu_wbuf[a].cbuf.head, \
+ mu_wbuf[a].cbuf.tail, MAX_BUF_SIZE)
+
+/*
+ * Used to calculate empty space in the read buffer
+ */
+#define GET_READSPACE(a) CIRC_SPACE(mu_rbuf[a].cbuf.head, \
+ mu_rbuf[a].cbuf.tail, MAX_BUF_SIZE)
+
+/*
+ * Used to calculate empty space in the write buffer
+ */
+#define GET_WRITESPACE(a) CIRC_SPACE(mu_wbuf[a].cbuf.head, \
+ mu_wbuf[a].cbuf.tail, MAX_BUF_SIZE)
+
+/*!
+ * There are 4 callback functions, one for each receive
+ * interrupt. When 'bind' function is called the callback
+ * functions are stored in an array and retrieved in the
+ * tasklets when a receive interrupt arrives
+ */
+static callbackfn rxcallback[NUM_MU_CHANNELS];
+
+/*!
+ * There are 4 callback functions, one for each transmit
+ * interrupt. When 'bind' function is called the callback
+ * functions are stored in an array and retrieved in the
+ * tasklets when a transmit interrupt arrives
+ */
+static callbackfn txcallback[NUM_MU_CHANNELS];
+
+/*!
+ * There are 4 callback functions, one for each general purpose
+ * interrupt. When 'bind' function is called the callback
+ * functions are stored in an array and retrieved in the
+ * tasklets when a general purpose interrupt arrives
+ */
+static callbackfn gpcallback[NUM_MU_CHANNELS];
+
+/*!
+ * Declaring lock for mutual exclusion
+ */
+static DEFINE_SPINLOCK(mu_lock);
+
+/*!
+ * Declaring lock to prevent access to
+ * allocated registers
+ */
+static DEFINE_SPINLOCK(alloc_lock);
+
+/*!
+ * Number of users waiting in suspendq
+ */
+static int swait = 0;
+
+/*!
+ * To indicate whether any of the MU devices are suspending
+ */
+static int suspend_flag = 0;
+
+/*!
+ * To restore the state of control register while resuming the device
+ * to the state it was before suspending it.
+ */
+static unsigned int mcr_suspend_state = 0;
+
+/*!
+ * Indicate block mode - 0 and non block mode - 1. Default is non-block mode
+ */
+static int blockmode[NUM_MU_CHANNELS] = { 1, 1, 1, 1 };
+
+/*!
+ * Device Kernel Buffers. Each channel has one read and write buffer.
+ */
+typedef struct {
+ /*!
+ * Buffer used to store data that was read or the data that is
+ * to be written
+ */
+ struct circ_buf cbuf;
+ /*!
+ * to indicate number of users waiting on blocking mode of the device
+ */
+ int pwait;
+} ring_buffer;
+
+ring_buffer mu_rbuf[NUM_MU_CHANNELS], mu_wbuf[NUM_MU_CHANNELS];
+
+/*!
+ * reg_allocated[] stores channel handlers for the allocated registers.
+ */
+static int reg_allocated[NUM_MU_CHANNELS] = { -1, -1, -1, -1 };
+
+static unsigned int wbits[NUM_MU_CHANNELS] = { AS_MUMSR_MTE0, AS_MUMSR_MTE1,
+ AS_MUMSR_MTE2, AS_MUMSR_MTE3
+};
+
+static unsigned int rbits[NUM_MU_CHANNELS] = { AS_MUMSR_MRF0, AS_MUMSR_MRF1,
+ AS_MUMSR_MRF2, AS_MUMSR_MRF3
+};
+
+static unsigned int read_en[NUM_MU_CHANNELS] = { AS_MUMCR_MRIE0, AS_MUMCR_MRIE1,
+ AS_MUMCR_MRIE2, AS_MUMCR_MRIE3
+};
+
+static unsigned int write_en[NUM_MU_CHANNELS] =
+ { AS_MUMCR_MTIE0, AS_MUMCR_MTIE1,
+ AS_MUMCR_MTIE2, AS_MUMCR_MTIE3
+};
+
+static unsigned int genp_en[NUM_MU_CHANNELS] = { AS_MUMCR_MGIE0, AS_MUMCR_MGIE1,
+ AS_MUMCR_MGIE2, AS_MUMCR_MGIE3
+};
+
+static unsigned int genp_pend[NUM_MU_CHANNELS] =
+ { AS_MUMSR_MGIP0, AS_MUMSR_MGIP1,
+ AS_MUMSR_MGIP2, AS_MUMSR_MGIP3
+};
+
+static unsigned int genp_req[NUM_MU_CHANNELS] =
+ { AS_MUMCR_MGIR0, AS_MUMCR_MGIR1,
+ AS_MUMCR_MGIR2, AS_MUMCR_MGIR3
+};
+
+static unsigned int read_reg[NUM_MU_CHANNELS] = { AS_MUMRR0, AS_MUMRR1,
+ AS_MUMRR2, AS_MUMRR3
+};
+
+static unsigned int write_reg[NUM_MU_CHANNELS] = { AS_MUMTR0, AS_MUMTR1,
+ AS_MUMTR2, AS_MUMTR3
+};
+
+/*!
+ * The readq and waitq are used by blocking read/write
+ */
+static wait_queue_head_t readq, writeq, suspendq, wait_before_closeq;
+
+/*!
+ * To enable or disable byte swapping
+ */
+static bool byte_swapping_rd[NUM_MU_CHANNELS] = { false, false, false, false };
+static bool byte_swapping_wr[NUM_MU_CHANNELS] = { false, false, false, false };
+
+/*
+ * Declaring Bottom-half handlers - one for GP, RX and TX interrupt lines
+ */
+static void mxc_mu_rxhandler(unsigned long someval);
+static void mxc_mu_txhandler(unsigned long someval);
+static void mxc_mu_gphandler(unsigned long someval);
+
+/*
+ * Tasklets Declarations
+ */
+/*
+ * Declaring tasklet to handle receive interrupts
+ */
+DECLARE_TASKLET(rxch0tasklet, (void *)mxc_mu_rxhandler, (unsigned long)0);
+DECLARE_TASKLET(rxch1tasklet, (void *)mxc_mu_rxhandler, (unsigned long)1);
+DECLARE_TASKLET(rxch2tasklet, (void *)mxc_mu_rxhandler, (unsigned long)2);
+DECLARE_TASKLET(rxch3tasklet, (void *)mxc_mu_rxhandler, (unsigned long)3);
+
+/*
+ * Declaring tasklet to handle transmit interrupts
+ */
+DECLARE_TASKLET(txch0tasklet, (void *)mxc_mu_txhandler, (unsigned long)0);
+DECLARE_TASKLET(txch1tasklet, (void *)mxc_mu_txhandler, (unsigned long)1);
+DECLARE_TASKLET(txch2tasklet, (void *)mxc_mu_txhandler, (unsigned long)2);
+DECLARE_TASKLET(txch3tasklet, (void *)mxc_mu_txhandler, (unsigned long)3);
+
+/*
+ * Declaring tasklet to handle general purpose interrupts
+ */
+DECLARE_TASKLET(gpch0tasklet, (void *)mxc_mu_gphandler, (unsigned long)0);
+DECLARE_TASKLET(gpch1tasklet, (void *)mxc_mu_gphandler, (unsigned long)1);
+DECLARE_TASKLET(gpch2tasklet, (void *)mxc_mu_gphandler, (unsigned long)2);
+DECLARE_TASKLET(gpch3tasklet, (void *)mxc_mu_gphandler, (unsigned long)3);
+
+/*!
+ * Allocating Resources:
+ * Allocating channels required to the caller if they are free.
+ * 'reg_allocated' variable holds all the registers that have been allocated
+ *
+ * @param chnum The channel number required is passed
+ *
+ * @return Returns unique channel handler associated with channel. This
+ * channel handler is used to validate the user accessing the
+ * channel.
+ * Returns channel handler - on success
+ * Returns negative value - on error
+ * If invalid channel is passed, it returns -ENODEV error.
+ * If no channel is available, returns -EBUSY.
+ */
+int mxc_mu_alloc_channel(int chnum)
+{
+ unsigned long flags;
+ int ret_val = -EBUSY;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ /* Acquiring Lock */
+ spin_lock_irqsave(&alloc_lock, flags);
+ if (reg_allocated[chnum] == -1) {
+ /*
+ * If channel requested is available, create
+ * channel handler and store in the array
+ */
+ reg_allocated[chnum] = BASE_NUM + chnum;
+ ret_val = BASE_NUM + chnum;
+ }
+ spin_unlock_irqrestore(&alloc_lock, flags);
+ return ret_val;
+}
+
+/*!
+ * Deallocating Resources:
+ *
+ * @param chand The channel handler associated with the channel
+ * that is to be freed is passed.
+ *
+ * @return Returns 0 on success and -1 if channel was not
+ * already allocated or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ */
+int mxc_mu_dealloc_channel(int chand)
+{
+ unsigned long flags;
+ int retval = -1;
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&alloc_lock, flags);
+ if (reg_allocated[chnum] == chand) {
+ /* deallocating */
+ reg_allocated[chnum] = -1;
+ /* clearing callback arrays */
+ rxcallback[chnum] = NULL;
+ txcallback[chnum] = NULL;
+ gpcallback[chnum] = NULL;
+ retval = 0;
+ }
+ spin_unlock_irqrestore(&alloc_lock, flags);
+ return retval;
+}
+
+/* NON BLOCKING READ/WRITE */
+
+/*!
+ * This function is called by other modules to read from one of the
+ * receive registers on the MCU side.
+ *
+ * @param chand The channel handler associated with the channel
+ * to be accessed is passed
+ * @param mu_msg Buffer where the read data has to be stored
+ * is passed by pointer as an argument
+ *
+ * @return Returns 0 on success or
+ * Returns NO_DATA error if there is no data to read or
+ * Returns NO_ACCESS error if the incorrect channel
+ * is accessed or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if the buffer passed is not allocated
+ */
+int mxc_mu_mcuread(int chand, char *mu_msg)
+{
+ /* mu_msg should be 4 bytes long */
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (mu_msg == NULL) {
+ return -1;
+ }
+ if ((readl(AS_MUMSR) & rbits[chnum]) == 0) {
+ return NO_DATA;
+ }
+ if (reg_allocated[chnum] == chand) {
+ *(unsigned int *)mu_msg = readl(read_reg[chnum]);
+ } else {
+ return NO_ACCESS;
+ }
+ if (byte_swapping_rd[chnum] == true) {
+ *(unsigned int *)mu_msg = swab32(*(unsigned int *)mu_msg);
+ }
+ return 0;
+}
+
+/*!
+ * This function is called by other modules to write to one of the
+ * transmit registers on the MCU side
+ *
+ * @param chand The channel handler associated with the channel
+ * to be accessed is passed
+ * @param mu_msg Buffer where the write data has to be stored
+ * is passed by pointer as an argument
+ *
+ * @return Returns 0 on success or
+ * Returns NO_DATA error if the register not empty or
+ * Returns NO_ACCESS error if the incorrect channel
+ * is accessed or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if the buffer passed is not allocated
+ */
+int mxc_mu_mcuwrite(int chand, char *mu_msg)
+{
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (mu_msg == NULL) {
+ return -1;
+ }
+ if (byte_swapping_wr[chnum] == true) {
+ /* Swap the bytes */
+ *(unsigned int *)mu_msg = swab32(*(unsigned int *)mu_msg);
+ }
+ if ((readl(AS_MUMSR) & wbits[chnum]) == 0) {
+ return NO_DATA;
+ }
+ /* Check whether the register is allocated */
+ if (reg_allocated[chnum] == chand) {
+ writel(*(unsigned int *)mu_msg, write_reg[chnum]);
+ } else {
+ return NO_ACCESS;
+ }
+ return 0;
+}
+
+/*!
+ * Tasklet used by the Interrupt service routine to handle the muxed
+ * receive interrupts. When an interrupt occurs, control from the top main
+ * interrupt service routine is transferred to this tasklet which sends the
+ * channel number to the call back function
+ *
+ * @param chnum index value indicating channel number
+ *
+ */
+static void mxc_mu_rxhandler(unsigned long chnum)
+{
+ if (rxcallback[chnum] != NULL) {
+ rxcallback[chnum] (chnum);
+ }
+}
+
+/*!
+ * Tasklet used by the Interrupt service routine to handle the muxed
+ * transmit interrupts. When an interrupt occurs, control from the top main
+ * interrupt service routine is transferred to this tasklet which sends the
+ * channel number to the call back function
+ *
+ * @param chnum index value indicating channel number
+ *
+ */
+static void mxc_mu_txhandler(unsigned long chnum)
+{
+ if (txcallback[chnum] != NULL) {
+ txcallback[chnum] (chnum);
+ }
+}
+
+/*!
+ * Tasklet used by the Interrupt service routine to handle the general
+ * purpose interrupts. When an interrupt occurs, control from the top main
+ * interrupt service routine is transferred to this tasklet which sends the
+ * channel number to the call back function
+ *
+ * @param chnum index value indicating channel number
+ *
+ */
+static void mxc_mu_gphandler(unsigned long chnum)
+{
+ if (gpcallback[chnum] != NULL) {
+ gpcallback[chnum] (chnum);
+ }
+}
+
+/*!
+ * Interrupt service routine registered to handle the individual general purpose
+ * interrupts or muxed. Interrupts are cleared in ISR before scheduling tasklet
+ *
+ * @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 mxc_mu_mcugphand(int irq, void *dev_id)
+{
+ int handled = 0;
+ unsigned int sreg1, sreg2, status = 0;
+
+ sreg1 = readl(AS_MUMSR);
+ sreg2 = readl(AS_MUMCR);
+ /* When General Purpose Interrupt occurs */
+ if ((sreg1 & AS_MUMSR_MGIP0) && (sreg2 & AS_MUMCR_MGIE0)) {
+ status |= (AS_MUMSR_MGIP0);
+ tasklet_schedule(&gpch0tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MGIP1) && (sreg2 & AS_MUMCR_MGIE1)) {
+ status |= (AS_MUMSR_MGIP1);
+ tasklet_schedule(&gpch1tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MGIP2) && (sreg2 & AS_MUMCR_MGIE2)) {
+ status |= (AS_MUMSR_MGIP2);
+ tasklet_schedule(&gpch2tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MGIP3) && (sreg2 & AS_MUMCR_MGIE3)) {
+ status |= (AS_MUMSR_MGIP3);
+ tasklet_schedule(&gpch3tasklet);
+ handled = 1;
+ }
+ writel(status, AS_MUMSR);
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * Interrupt service routine registered to handle the muxed receive
+ * interrupts. Interrupts disabled inside ISR before scheduling tasklet
+ *
+ * @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 mxc_mu_mcurxhand(int irq, void *dev_id)
+{
+ int handled = 0;
+ unsigned int sreg1, sreg2, control;
+
+ sreg1 = readl(AS_MUMSR);
+ sreg2 = readl(AS_MUMCR);
+ control = sreg2;
+ /* When Receive Interrupt occurs */
+ if ((sreg1 & AS_MUMSR_MRF0) && (sreg2 & AS_MUMCR_MRIE0)) {
+ control &= ~(AS_MUMCR_MRIE0);
+ tasklet_schedule(&rxch0tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MRF1) && (sreg2 & AS_MUMCR_MRIE1)) {
+ control &= ~(AS_MUMCR_MRIE1);
+ tasklet_schedule(&rxch1tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MRF2) && (sreg2 & AS_MUMCR_MRIE2)) {
+ control &= ~(AS_MUMCR_MRIE2);
+ tasklet_schedule(&rxch2tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MRF3) && (sreg2 & AS_MUMCR_MRIE3)) {
+ control &= ~(AS_MUMCR_MRIE3);
+ tasklet_schedule(&rxch3tasklet);
+ handled = 1;
+ }
+ writel(control, AS_MUMCR);
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * Interrupt service routine registered to handle the muxed transmit interrupts
+ * Interrupts disabled inside ISR before scheduling tasklet
+ *
+ * @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 mxc_mu_mcutxhand(int irq, void *dev_id)
+{
+ int handled = 0;
+ unsigned int sreg1, sreg2, control;
+
+ sreg1 = readl(AS_MUMSR);
+ sreg2 = readl(AS_MUMCR);
+ control = sreg2;
+ /* When Transmit Interrupt occurs */
+ if ((sreg1 & AS_MUMSR_MTE0) && (sreg2 & AS_MUMCR_MTIE0)) {
+ control &= ~(AS_MUMCR_MTIE0);
+ tasklet_schedule(&txch0tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MTE1) && (sreg2 & AS_MUMCR_MTIE1)) {
+ control &= ~(AS_MUMCR_MTIE1);
+ tasklet_schedule(&txch1tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MTE2) && (sreg2 & AS_MUMCR_MTIE2)) {
+ control &= ~(AS_MUMCR_MTIE2);
+ tasklet_schedule(&txch2tasklet);
+ handled = 1;
+ }
+ if ((sreg1 & AS_MUMSR_MTE3) && (sreg2 & AS_MUMCR_MTIE3)) {
+ control &= ~(AS_MUMCR_MTIE3);
+ tasklet_schedule(&txch3tasklet);
+ handled = 1;
+ }
+ writel(control, AS_MUMCR);
+ return IRQ_RETVAL(handled);
+}
+
+/*!
+ * This function is used by other modules to issue DSP hardware reset.
+ *
+ */
+int mxc_mu_dsp_reset(void)
+{
+ unsigned long flags;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ writel((readl(AS_MUMCR) | AS_MUMCR_DHR), AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is used by other modules to deassert DSP hardware reset
+ */
+int mxc_mu_dsp_deassert(void)
+{
+ unsigned long flags;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ writel((readl(AS_MUMCR) & (~AS_MUMCR_DHR)), AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is used by other modules to issue DSP Non-Maskable
+ * Interrupt to the DSP
+ */
+int mxc_mu_dsp_nmi(void)
+{
+ unsigned long flags;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ writel((readl(AS_MUMCR) | AS_MUMCR_DNMI), AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is used by other modules to retrieve the DSP reset state.
+ *
+ * @return Returns 1 if DSP in reset state and 0 otherwise
+ */
+int mxc_mu_dsp_reset_status(void)
+{
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if ((readl(AS_MUMSR) & AS_MUMSR_DRS) != 0) {
+ /* 1 implies the DSP side of MU is in reset state */
+ return 1;
+ } else {
+ /* 0 implies the DSP side of MU is not in reset state */
+ return 0;
+ }
+}
+
+/*!
+ * This function is used by other modules to retrieve the DSP Power Mode.
+ *
+ * @return Returns a value from which power mode of the DSP side of
+ * of MU unit can be inferred
+ * 0 - Run mode, 1 - Wait mode
+ * 2 - Stop mode, 3 - DSM mode
+ */
+unsigned int mxc_mu_dsp_pmode_status(void)
+{
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ return (readl(AS_MUMSR) & (AS_MUMSR_DPM1 | AS_MUMSR_DPM0) >> 5);
+}
+
+/*!
+ * This function is used by other modules to reset the MU Unit. This would reset
+ * both the MCU side and the DSP side
+ *
+ */
+int mxc_mu_reset(void)
+{
+ unsigned long flags;
+
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ writel((readl(AS_MUMCR) | AS_MUMCR_MMUR), AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is called by unbind function of this module and
+ * can also be called from other modules to enable desired
+ * receive Interrupt
+ *
+ * @param chand The channel handler associated with the channel
+ * whose interrupt to be disabled is passed
+ * @param muoper The value passed is TX, RX, GP
+ *
+ * @return Returns 0 on success or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if muoper is other than RX, TX or GP
+ */
+int mxc_mu_intdisable(int chand, enum mu_oper muoper)
+{
+ unsigned int status;
+ unsigned long flags;
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ status = readl(AS_MUMCR);
+ switch (muoper) {
+ case RX:
+ status &= ~(read_en[chnum]);
+ break;
+ case TX:
+ status &= ~(write_en[chnum]);
+ break;
+ case GP:
+ status &= ~(genp_en[chnum]);
+ break;
+ default:
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return -1;
+ }
+ writel(status, AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is called by other modules to unbind their
+ * call back functions
+ *
+ * @param chand The channel handler associated with the channel
+ * to be accessed is passed
+ * @param muoper The value passed is TX, RX, GP
+ *
+ * @return Returns 0 on success or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if muoper is other than RX, TX or GP, or
+ * if disabling interrupt failed
+ */
+int mxc_mu_unbind(int chand, enum mu_oper muoper)
+{
+ int result = 0;
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (reg_allocated[chnum] == chand) {
+ if (mxc_mu_intdisable(chand, muoper) != 0) {
+ return -1;
+ }
+ switch (muoper) {
+ case RX:
+ rxcallback[chnum] = NULL;
+ break;
+ case TX:
+ txcallback[chnum] = NULL;
+ break;
+ case GP:
+ gpcallback[chnum] = NULL;
+ break;
+ default:
+ result = -1;
+ }
+ }
+ return result;
+}
+
+/*!
+ * This function is called by the user and other modules to enable desired
+ * interrupt
+ *
+ * @param chand The channel handler associated with the channel
+ * to be accessed is passed
+ * @param muoper The value passed is TX, RX, GP
+ *
+ * @return Returns 0 on success or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if muoper is other than RX, TX or GP
+ */
+int mxc_mu_intenable(int chand, enum mu_oper muoper)
+{
+ unsigned long status = 0;
+ unsigned long flags;
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ spin_lock_irqsave(&mu_lock, flags);
+ status = readl(AS_MUMCR);
+ switch (muoper) {
+ case RX:
+ status |= (read_en[chnum]);
+ break;
+ case TX:
+ status |= (write_en[chnum]);
+ break;
+ case GP:
+ status |= (genp_en[chnum]);
+ break;
+ default:
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return -1;
+ }
+ writel(status, AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+ return 0;
+}
+
+/*!
+ * This function is called by other modules to bind their
+ * call back functions.
+ *
+ * @param chand The channel handler associated with the channel
+ * to be accessed is passed
+ * @param callback the caller's callback function
+ * @param muoper The value passed is TX, RX, GP
+ *
+ * @return Returns 0 on success or ENODEV error if invalid
+ * channel is obtained from the channel handler
+ * Returns -1 if muoper is other than RX, TX or GP
+ */
+int mxc_mu_bind(int chand, callbackfn callback, enum mu_oper muoper)
+{
+
+ int chnum = chand - BASE_NUM;
+
+ if (chnum < 0 || chnum > 3) {
+ return -ENODEV;
+ }
+ if (suspend_flag == 1) {
+ return -EBUSY;
+ }
+ if (reg_allocated[chnum] == chand) {
+ switch (muoper) {
+ case RX:
+ rxcallback[chnum] = callback;
+ break;
+ case TX:
+ txcallback[chnum] = callback;
+ break;
+ case GP:
+ gpcallback[chnum] = callback;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * This function is used to copy data from kernel space buffer
+ * to user space buffer
+ */
+static int mcu_copytouser(int count, int minor, char *userbuff)
+{
+ int bytes_available = 0, actual_bytes = 0, checkval = 0;
+
+ while (1) {
+ bytes_available = CIRC_CNT_TO_END(mu_rbuf[minor].cbuf.head,
+ mu_rbuf[minor].cbuf.tail,
+ MAX_BUF_SIZE);
+ /*
+ * If the number of bytes already in the buffer is greater than
+ * count then read count from buffer else read what is in the buffer
+ */
+ if (count < bytes_available) {
+ bytes_available = count;
+ }
+ if (bytes_available <= 0) {
+ break;
+ }
+ checkval = copy_to_user(userbuff,
+ &mu_rbuf[minor].cbuf.buf[mu_rbuf[minor].
+ cbuf.tail],
+ bytes_available);
+ if (checkval) {
+ break;
+ }
+ mu_rbuf[minor].cbuf.tail = (mu_rbuf[minor].cbuf.tail +
+ bytes_available) & (MAX_BUF_SIZE -
+ 1);
+
+ userbuff += bytes_available;
+ count -= bytes_available;
+ actual_bytes += bytes_available;
+ }
+ /*
+ * Return the number of bytes copied
+ */
+ return actual_bytes;
+}
+
+/*
+ * This function is used to copy data from user space buffer
+ * to kernel space buffer
+ */
+static int mcu_copyfromuser(int count, int minor, const char *userbuff)
+{
+ int space_available = 0, bytes_copied = 0, checkval = 0;
+
+ while (1) {
+ space_available = CIRC_SPACE_TO_END(mu_wbuf[minor].cbuf.head,
+ mu_wbuf[minor].cbuf.tail,
+ MAX_BUF_SIZE);
+ /*
+ * If the space available in the kernel space buffer
+ * is more than required i.e., count then number of bytes
+ * copied from the user space buffer is count else available empty
+ * space for transmission
+ */
+ if (space_available >= count) {
+ space_available = count;
+ }
+ if ((space_available % CH_SIZE) != 0) {
+ space_available = space_available -
+ (space_available % CH_SIZE);
+ }
+ if (space_available < CH_SIZE) {
+ break;
+ }
+ checkval =
+ copy_from_user(&mu_wbuf[minor].cbuf.
+ buf[mu_wbuf[minor].cbuf.head], userbuff,
+ space_available);
+ if (checkval) {
+ break;
+ }
+ mu_wbuf[minor].cbuf.head =
+ (mu_wbuf[minor].cbuf.head +
+ space_available) & (MAX_BUF_SIZE - 1);
+
+ userbuff += space_available;
+ count -= space_available;
+ bytes_copied += space_available;
+ }
+
+ return bytes_copied;
+}
+
+/*!
+ * This function allows the caller to determine whether it can
+ * read from or write to one or more open files without blocking.
+ *
+ * @param filp Pointer to device file structure
+ * @param wait used by poll_wait to indicate a change in the
+ * poll status.
+ *
+ * @return the bit mask describing which operations could
+ * be completed immediately.
+ */
+static unsigned int mxc_mu_poll(struct file *filep, poll_table * wait)
+{
+ unsigned int minor;
+ unsigned int mask = 0;
+
+ minor = MINOR(filep->f_dentry->d_inode->i_rdev);
+
+ poll_wait(filep, &writeq, wait);
+ poll_wait(filep, &readq, wait);
+
+ if (GET_READBYTES(minor)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (GET_WRITESPACE(minor) >= CH_SIZE) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ return mask;
+}
+
+/*!
+ * The read function is available to the user-space to perform
+ * a read operation
+ *
+ * @param filp Pointer to device file structure
+ * @param userbuff User buffer, where read data to be placed.
+ * @param count Size of the requested data transfer
+ * @param offp File position where the user is accessing.
+ *
+ * @return Returns number of bytes read or
+ * Returns -EINVAL if the number of bytes
+ * requested is not a multiple of channel
+ * receive or transmit size or
+ * Returns -EAGAIN for a non-block read when no data
+ * is ready in the buffer or in the register or
+ * Returns -EFAULT if copy_to_user failed
+ */
+static ssize_t mxc_mu_read(struct file *filp,
+ char *userbuff, size_t count, loff_t * offp)
+{
+ int total_bytestocopy = 0;
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ /*
+ * If the count value requested is not multiple of channel size then
+ * return error
+ */
+ if (((count % CH_SIZE) != 0) || (count == 0)) {
+ return -EINVAL; /* error */
+ }
+ total_bytestocopy = mcu_copytouser(count, minor, userbuff);
+ /*
+ * Enable the interrupt to read from the register
+ */
+ mxc_mu_intenable(reg_allocated[minor], RX);
+ if (total_bytestocopy != 0) {
+ return total_bytestocopy;
+ }
+ /*
+ * If there is no data in buffer and number of bytes
+ * copied to user space buffer is 0, then block until at least
+ * 4 bytes are read incase of block read else, return immediately
+ * incase of nonblock read
+ */
+ if (!(filp->f_flags & O_NONBLOCK)) {
+ while (GET_READBYTES(minor) == 0) {
+ mu_rbuf[minor].pwait++;
+ /* Block */
+ if (wait_event_interruptible(readq,
+ (GET_READBYTES(minor) !=
+ 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ } else {
+ /* Return error for Non-Block read */
+ return -EAGAIN;
+ }
+ /*
+ * Some data is now available in the register,
+ * read the data and copy to the user
+ */
+ return (mcu_copytouser(count, minor, userbuff));
+}
+
+/*!
+ * The write function is available to the user-space to perform
+ * a write operation
+ *
+ * @param filp Pointer to device file structure
+ * @param userbuff User buffer, where read data to be placed.
+ * @param count Size of the requested data transfer
+ * @param offp File position where the user is accessing.
+ *
+ * @return Returns number of bytes read or
+ * Returns -EINVAL if the number of bytes
+ * requested is not a multiple of channel
+ * receive or transmit size or
+ * Returns -EAGAIN for a non-block read when no data
+ * is ready in the buffer or in the register or
+ * Returns -EFAULT if copy_to_user failed
+ */
+static ssize_t mxc_mu_write(struct file *filp, const char *userbuff, size_t
+ count, loff_t * offp)
+{
+ int totalbytes_copied = 0;
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ /*
+ * If the count value requested is not multiple of channel size then
+ * return error
+ */
+ if (((count % CH_SIZE) != 0) || (count == 0)) {
+ return -EINVAL; /* error */
+ }
+ totalbytes_copied = mcu_copyfromuser(count, minor, userbuff);
+ /* Enable the interrupt before return so that data is transmitted */
+ mxc_mu_intenable(reg_allocated[minor], TX);
+ /*
+ * If number of bytes copied to the kernel space buffer is greater than
+ * 0, return immediately
+ */
+ if (totalbytes_copied != 0) {
+ return totalbytes_copied;
+ }
+ /* If there is no space in buffer and number of bytes
+ * copied from user space buffer is 0, then block until at least
+ * 4 bytes are written incase of block write else, return
+ * immediately incase of nonblock write
+ */
+ if (!(filp->f_flags & O_NONBLOCK)) {
+ while (GET_WRITESPACE(minor) < CH_SIZE) {
+ mu_wbuf[minor].pwait++;
+ if (wait_event_interruptible(writeq,
+ (GET_WRITESPACE(minor) >=
+ CH_SIZE))) {
+ return -ERESTARTSYS;
+ }
+ }
+ } else {
+ /* Return error incase of non-block write */
+ return -EAGAIN;
+ }
+ /*
+ * Some space is now available since some
+ * data has been transmitted. So, copy data from
+ * the user space buffer to kernel space buffer,
+ */
+ return (mcu_copyfromuser(count, minor, userbuff));
+}
+
+/*!
+ * The write callback function is used by this module to perform any
+ * write requests from user-space
+ *
+ * @param chnum channel number whose register has to be accessed
+ */
+static void mxc_mu_writecb(int chnum)
+{
+ char *message;
+
+ /*
+ * While there are more bytes to transfer and
+ * register is empty, continue transmitting
+ */
+ while ((GET_WRITEBYTES(chnum) != 0) && (readl(AS_MUMSR) & wbits[chnum])) {
+ /* Maximum of 4 bytes everytime */
+ message = &mu_wbuf[chnum].cbuf.buf[mu_wbuf[chnum].cbuf.tail];
+ mu_wbuf[chnum].cbuf.tail =
+ (mu_wbuf[chnum].cbuf.tail + CH_SIZE) & (MAX_BUF_SIZE - 1);
+ mxc_mu_mcuwrite(reg_allocated[chnum], message);
+ /* Emptying write buffer */
+ }
+ /*
+ * Enable the interrupt if more data has to be transmitted.
+ * Since interrupts are disabled inside ISR.
+ */
+ if (GET_WRITEBYTES(chnum) != 0) {
+ mxc_mu_intenable(reg_allocated[chnum], TX);
+ }
+ /* Wake up the sleeping process if the buffer gets emptied */
+ if (blockmode[chnum] == 0 && mu_wbuf[chnum].pwait > 0 &&
+ GET_WRITESPACE(chnum) >= CH_SIZE) {
+ mu_wbuf[chnum].pwait--;
+ /* Wake up */
+ wake_up_interruptible(&writeq);
+ }
+}
+
+/*!
+ * The read callback function is used by this module to perform any
+ * read requests from user-space
+ *
+ * @param chnum channel number whose register has to be accessed
+ */
+static void mxc_mu_readcb(int chnum)
+{
+ char message[CH_SIZE];
+ int index;
+
+ /*
+ * While there more bytes can be read and if
+ * buffer is empty
+ */
+ while ((GET_READSPACE(chnum) >= CH_SIZE)
+ && (readl(AS_MUMSR) & rbits[chnum])) {
+ mxc_mu_mcuread(reg_allocated[chnum], message);
+ index = mu_rbuf[chnum].cbuf.head;
+ *(int *)(&(mu_rbuf[chnum].cbuf.buf[index])) = *(int *)message;
+ mu_rbuf[chnum].cbuf.head =
+ (mu_rbuf[chnum].cbuf.head + CH_SIZE) & (MAX_BUF_SIZE - 1);
+ }
+ /*
+ * If no empty space in buffer to store the data that
+ * has been read, then disable the interrupt else
+ * enable the interrupt
+ */
+ (GET_READSPACE(chnum) <
+ CH_SIZE) ? mxc_mu_intdisable(reg_allocated[chnum],
+ RX) :
+mxc_mu_intenable(reg_allocated[chnum], RX);
+ /* Wake up the sleeping process if data is available */
+ if (blockmode[chnum] == 0 && mu_rbuf[chnum].pwait > 0 &&
+ GET_READBYTES(chnum) != 0) {
+ mu_rbuf[chnum].pwait--;
+ /* Wake up */
+ wake_up_interruptible(&readq);
+ }
+}
+
+/*!
+ * The general purpose callback function is used by this module to
+ * perform any general purpose interrupt requests from user-space
+ *
+ * @param chnum channel number whose register has to be accessed
+ */
+static void mxc_mu_genpurposecb(int chnum)
+{
+ writel((readl(AS_MUMSR) | (genp_pend[chnum])), AS_MUMSR);
+}
+
+/*!
+ * This function is called when an ioctl call is made from user space.
+ *
+ * @param inode Pointer to device inode
+ * @param file Pointer to device file structure
+ * @param cmd Ioctl command
+ * @param arg Ioctl argument
+ *
+ * @return The function returns 0 on success and -EINVAL on
+ * failure when cmd is other than what is specified
+ */
+int mxc_mu_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct inode *i_node = file->f_dentry->d_inode;
+ unsigned int minor = MINOR(i_node->i_rdev);
+ int i = 0;
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ switch (cmd) {
+ case SENDINT:
+ writel(genp_req[minor], AS_MUMCR);
+ break;
+ case SENDNMI:
+ mxc_mu_dsp_nmi();
+ break;
+ case SENDDSPRESET:
+ mxc_mu_dsp_reset();
+ for (i = 0; i < 20; i++) ;
+ mxc_mu_dsp_deassert();
+ while (mxc_mu_dsp_reset_status() != 0) ;
+ break;
+ case SENDMURESET:
+ mxc_mu_reset();
+ break;
+ case RXENABLE:
+ mxc_mu_intenable(reg_allocated[minor], RX);
+ break;
+ case TXENABLE:
+ mxc_mu_intenable(reg_allocated[minor], TX);
+ break;
+ case GPENABLE:
+ mxc_mu_intenable(reg_allocated[minor], GP);
+ break;
+ case BYTESWAP_RD:
+ byte_swapping_rd[minor] = true;
+ break;
+ case BYTESWAP_WR:
+ byte_swapping_wr[minor] = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * This function is called to close the device. The MCU side of MU interrupts
+ * are disabled. Any allocated buffers are freed
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success or
+ * returns -ENODEV if incorrect channel is accessed
+ *
+ */
+static int mxc_mu_close(struct inode *inode, struct file *filp)
+{
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ /* Wait to complete any pending writes */
+ /* Block until the write buffer is empty */
+ while (GET_WRITEBYTES(minor) != 0) {
+ if (wait_event_interruptible_timeout
+ (wait_before_closeq, (GET_WRITEBYTES(minor) == 0),
+ MAX_SCHEDULE_TIMEOUT)) {
+ return -ERESTARTSYS;
+ }
+ }
+ if (mxc_mu_dealloc_channel(reg_allocated[minor]) != 0) {
+ return -ENODEV;
+ }
+ kfree(mu_rbuf[minor].cbuf.buf);
+ return 0;
+}
+
+/*!
+ * This function is called when the MU driver is opened. This function does
+ * the initialization of the device.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success or a negative value if
+ * channel was not allocated or if kmalloc failed
+ */
+static int mxc_mu_open(struct inode *inode, struct file *filp)
+{
+ int chand;
+ unsigned int minor = MINOR(inode->i_rdev);
+
+ while (suspend_flag == 1) {
+ swait++;
+ /* Block if the device is suspended */
+ if (wait_event_interruptible(suspendq, (suspend_flag == 0))) {
+ return -ERESTARTSYS;
+ }
+ }
+ chand = mxc_mu_alloc_channel(minor);
+ if (chand < 0) {
+ return chand;
+ }
+ mxc_mu_bind(reg_allocated[minor], &mxc_mu_readcb, RX);
+ mxc_mu_bind(reg_allocated[minor], &mxc_mu_writecb, TX);
+ mxc_mu_bind(reg_allocated[minor], &mxc_mu_genpurposecb, GP);
+ mxc_mu_intdisable(reg_allocated[minor], TX);
+ /*
+ * Read buffer from 0 to MAX_BUF_SIZE
+ * Write buffer from MAX_BUF_SIZE to 2*MAX_BUF_SIZE
+ */
+ mu_rbuf[minor].cbuf.buf = kmalloc((2 * MAX_BUF_SIZE), GFP_KERNEL);
+ mu_wbuf[minor].cbuf.buf = mu_rbuf[minor].cbuf.buf + MAX_BUF_SIZE;
+ if (mu_rbuf[minor].cbuf.buf == NULL) {
+ reg_allocated[minor] = -1;
+ return -1;
+ }
+ mu_rbuf[minor].cbuf.tail = mu_rbuf[minor].cbuf.head = 0;
+ mu_wbuf[minor].cbuf.tail = mu_wbuf[minor].cbuf.head = 0;
+
+ byte_swapping_rd[minor] = false;
+ byte_swapping_wr[minor] = false;
+
+ mu_rbuf[minor].pwait = mu_rbuf[minor].pwait = 0;
+ if (filp->f_flags & O_NONBLOCK) {
+ blockmode[minor] = 1;
+ } else {
+ blockmode[minor] = 0;
+ }
+ mxc_mu_intenable(reg_allocated[minor], RX);
+ return 0;
+}
+
+/*!
+ * This function is called to put the MU in a low power state.
+ *
+ * @param pdev the device structure used to give information on which MU
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_mu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ unsigned long flags;
+
+ suspend_flag = 1;
+ spin_lock_irqsave(&mu_lock, flags);
+ mcr_suspend_state = readl(AS_MUMCR);
+ /* Disabling all receive, transmit, gen. purpose interrupts */
+ writel((mcr_suspend_state & 0x0000FFFF), AS_MUMCR);
+ spin_unlock_irqrestore(&mu_lock, flags);
+
+ /* Turn off clock */
+ clk_disable(mu_clkp);
+
+ return 0;
+}
+
+/*!
+ * 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 mxc_mu_resume(struct platform_device *pdev)
+{
+ unsigned long flags;
+
+ /* Turn on clock */
+ clk_enable(mu_clkp);
+
+ spin_lock_irqsave(&mu_lock, flags);
+ /* Re-enable interrupts or restore state of interrupts */
+ writel(mcr_suspend_state, AS_MUMCR);
+ while (swait > 0) {
+ swait--;
+ wake_up_interruptible(&suspendq);
+ }
+ spin_unlock_irqrestore(&mu_lock, flags);
+ suspend_flag = 0;
+
+ return 0;
+}
+
+static struct file_operations mu_fops = {
+ .owner = THIS_MODULE,
+ .read = mxc_mu_read,
+ .write = mxc_mu_write,
+ .ioctl = mxc_mu_ioctl,
+ .open = mxc_mu_open,
+ .release = mxc_mu_close,
+ .poll = mxc_mu_poll,
+};
+
+/*!
+ * This function is called while loading the module to initiate message
+ * transfers. This function initializes all the registers.
+ */
+static void mxc_mu_load_mod(void)
+{
+ /* Initializing wait queues */
+ init_waitqueue_head(&readq);
+ init_waitqueue_head(&writeq);
+ init_waitqueue_head(&suspendq);
+ init_waitqueue_head(&wait_before_closeq);
+ /* Setting the status and control registers to default values */
+ writel(0xffff, AS_MUMSR);
+ writel(0, AS_MUMCR);
+}
+
+/*!
+ * This function is called while unloading driver.
+ * This resets all the registers and disables
+ * interrupts.
+ */
+static void mxc_mu_unload_mod(void)
+{
+ /* setting the status and control registers to default values */
+ writel(0xffff, AS_MUMSR);
+ writel(0, AS_MUMCR);
+}
+
+/*!
+ * This function is used to load the module and all the interrupt lines
+ * are requested.
+ *
+ * @return Returns an Integer on success
+ */
+static int __init mxc_mu_probe(struct platform_device *pdev)
+{
+ /* Initializing resources allocated */
+ int rx_irq, tx_irq, irq;
+ int max_res;
+ int i;
+ struct class_device *temp_class;
+
+ max_res = pdev->num_resources;
+ rx_irq = platform_get_irq(pdev, 0);
+ tx_irq = platform_get_irq(pdev, 1);
+ if (rx_irq == NO_IRQ || tx_irq == NO_IRQ)
+ return -ENXIO;
+
+ mu_clkp = clk_get(&pdev->dev, "mu_clk");
+ if (IS_ERR(mu_clkp))
+ return -ENXIO;
+ clk_enable(mu_clkp);
+
+ if ((mu_major = register_chrdev(0, "mxc_mu", &mu_fops)) < 0) {
+ printk(KERN_NOTICE
+ "Can't allocate major number for MU Devices.\n");
+ return -EAGAIN;
+ }
+
+ mxc_mu_class = class_create(THIS_MODULE, "mxc_mu");
+
+ if (IS_ERR(mxc_mu_class)) {
+ printk(KERN_ERR "Error creating mu class.\n");
+ unregister_chrdev(mu_major, "mxc_mu");
+ return PTR_ERR(mxc_mu_class);
+ }
+
+ mxc_mu_load_mod();
+
+ for (i = 0; i <= 3; i++) {
+ temp_class =
+ class_device_create(mxc_mu_class, NULL, MKDEV(mu_major, i),
+ NULL, "mxc_mu%u", i);
+
+ if (IS_ERR(temp_class))
+ goto err_out1;
+ }
+
+ if (request_irq(rx_irq, mxc_mu_mcurxhand, 0, "mxc_mu_rx", NULL) != 0)
+ goto err_out1;
+
+ if (request_irq(tx_irq, mxc_mu_mcutxhand, 0, "mxc_mu_tx", NULL) != 0)
+ goto err_out2;
+
+ for (i = 2; i < max_res; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq == NO_IRQ)
+ goto err_out3;
+
+ if (request_irq(irq, mxc_mu_mcugphand, 0, "mxc_mu_mcug", NULL)
+ != 0) {
+ printk(KERN_ERR "mxc_mu: request_irq for %d failed\n",
+ irq);
+ goto err_out3;
+ }
+ }
+
+ return 0;
+
+ err_out3:
+ for (--i; i >= 2; i--) {
+ irq = platform_get_irq(pdev, i);
+ free_irq(irq, NULL);
+ }
+ free_irq(tx_irq, NULL);
+
+ err_out2:
+ free_irq(rx_irq, NULL);
+
+ err_out1:
+ for (i = 0; i <= 3; i++) {
+ class_device_destroy(mxc_mu_class, MKDEV(mu_major, i));
+ }
+ mxc_mu_unload_mod();
+ class_destroy(mxc_mu_class);
+ unregister_chrdev(mu_major, "mxc_mu");
+
+ return -1;
+}
+
+/*!
+ * This function is used to unload the module and all interrupt lines are freed
+ */
+static void __exit mxc_mu_remove(struct platform_device *pdev)
+{
+ int irq, max_res, i;
+ max_res = pdev->num_resources;
+
+ /* Freeing the resources allocated */
+ mxc_mu_unload_mod();
+ clk_disable(mu_clkp);
+ clk_put(mu_clkp);
+ for (i = 0; i < max_res; i++) {
+ irq = platform_get_irq(pdev, i);
+ free_irq(irq, NULL);
+ }
+ for (i = 0; i <= 3; i++) {
+ class_device_destroy(mxc_mu_class, MKDEV(mu_major, i));
+ }
+ class_destroy(mxc_mu_class);
+ unregister_chrdev(mu_major, "mxc_mu");
+
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_mu_driver = {
+ .driver = {
+ .name = "mxc_mu",
+ },
+ .probe = mxc_mu_probe,
+ .remove = __exit_p(mxc_mu_remove),
+ .suspend = mxc_mu_suspend,
+ .resume = mxc_mu_resume,
+};
+
+/*
+ * Main initialization routine
+ */
+static int __init mxc_mu_init(void)
+{
+ /* Register the device driver structure. */
+ pr_info("MXC MU Driver %s\n", DVR_VER);
+ if (platform_driver_register(&mxc_mu_driver) != 0) {
+ printk(KERN_ERR "Driver register failed for mxc_mu_driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/*
+ * Clean up routine
+ */
+static void __exit mxc_mu_cleanup(void)
+{
+ /* Unregister the device structure */
+ platform_driver_unregister(&mxc_mu_driver);
+ printk(KERN_INFO "MU Driver Module Unloaded\n");
+}
+
+module_init(mxc_mu_init);
+module_exit(mxc_mu_cleanup);
+
+EXPORT_SYMBOL(mxc_mu_bind);
+EXPORT_SYMBOL(mxc_mu_unbind);
+EXPORT_SYMBOL(mxc_mu_alloc_channel);
+EXPORT_SYMBOL(mxc_mu_dealloc_channel);
+EXPORT_SYMBOL(mxc_mu_mcuread);
+EXPORT_SYMBOL(mxc_mu_mcuwrite);
+EXPORT_SYMBOL(mxc_mu_intenable);
+EXPORT_SYMBOL(mxc_mu_intdisable);
+EXPORT_SYMBOL(mxc_mu_reset);
+EXPORT_SYMBOL(mxc_mu_dsp_reset);
+EXPORT_SYMBOL(mxc_mu_dsp_deassert);
+EXPORT_SYMBOL(mxc_mu_dsp_nmi);
+EXPORT_SYMBOL(mxc_mu_dsp_reset_status);
+EXPORT_SYMBOL(mxc_mu_dsp_pmode_status);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Messaging Unit Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/mxc_mu_reg.h b/drivers/char/mxc_mu_reg.h
new file mode 100644
index 000000000000..2723e8707ed4
--- /dev/null
+++ b/drivers/char/mxc_mu_reg.h
@@ -0,0 +1,105 @@
+/*
+ * 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_mu_reg.h
+ *
+ * @brief This file provides all the Register Addresses, Bit definitions.
+ *
+ * @ingroup MU
+ */
+#ifndef __MXC_MU_REG_H__
+
+#define __MXC_MU_REG_H__
+
+#include <asm/hardware.h>
+
+/*
+ * Address offsets of the MU MCU side transmit registers
+ */
+#define AS_MUMTR0 IO_ADDRESS(MU_BASE_ADDR + 0x000) /* Transmit register 0 */
+#define AS_MUMTR1 IO_ADDRESS(MU_BASE_ADDR + 0x004) /* Transmit register 1 */
+#define AS_MUMTR2 IO_ADDRESS(MU_BASE_ADDR + 0x008) /* Transmit register 2 */
+#define AS_MUMTR3 IO_ADDRESS(MU_BASE_ADDR + 0x00c) /* Transmit register 3 */
+
+/*
+ * Address offsets of the MU MCU side receive registers
+ */
+#define AS_MUMRR0 IO_ADDRESS(MU_BASE_ADDR + 0x010) /* Receive register 0 */
+#define AS_MUMRR1 IO_ADDRESS(MU_BASE_ADDR + 0x014) /* Receive register 1 */
+#define AS_MUMRR2 IO_ADDRESS(MU_BASE_ADDR + 0x018) /* Receive register 2 */
+#define AS_MUMRR3 IO_ADDRESS(MU_BASE_ADDR + 0x01c) /* Receive register 3 */
+
+/*
+ * Address offset of the MU MCU side status register
+ */
+#define AS_MUMSR IO_ADDRESS(MU_BASE_ADDR + 0x020) /* Status register */
+
+/*
+ * Address offset of the MU MCU side control register
+ */
+#define AS_MUMCR IO_ADDRESS(MU_BASE_ADDR + 0x024) /* Control register */
+
+/*
+ * Bit definitions of MSR
+ */
+#define AS_MUMSR_MGIP0 0x80000000
+#define AS_MUMSR_MGIP1 0x40000000
+#define AS_MUMSR_MGIP2 0x20000000
+#define AS_MUMSR_MGIP3 0x10000000
+#define AS_MUMSR_MRF0 0x08000000
+#define AS_MUMSR_MRF1 0x04000000
+#define AS_MUMSR_MRF2 0x02000000
+#define AS_MUMSR_MRF3 0x01000000
+#define AS_MUMSR_MTE0 0x00800000
+#define AS_MUMSR_MTE1 0x00400000
+#define AS_MUMSR_MTE2 0x00200000
+#define AS_MUMSR_MTE3 0x00100000
+#define AS_MUMSR_MFUP 0x00000100
+#define AS_MUMSR_DRS 0x00000080
+#define AS_MUMSR_DPM1 0x00000040
+#define AS_MUMSR_DPM0 0x00000020
+#define AS_MUMSR_MEP 0x00000010
+#define AS_MUMSR_MNMIC 0x00000008
+#define AS_MUMSR_MF2 0x00000004
+#define AS_MUMSR_MF1 0x00000002
+#define AS_MUMSR_MF0 0x00000001
+
+/*
+ * Bit definitions of MCR
+ */
+#define AS_MUMCR_MGIE0 0x80000000
+#define AS_MUMCR_MGIE1 0x40000000
+#define AS_MUMCR_MGIE2 0x20000000
+#define AS_MUMCR_MGIE3 0x10000000
+#define AS_MUMCR_MRIE0 0x08000000
+#define AS_MUMCR_MRIE1 0x04000000
+#define AS_MUMCR_MRIE2 0x02000000
+#define AS_MUMCR_MRIE3 0x01000000
+#define AS_MUMCR_MTIE0 0x00800000
+#define AS_MUMCR_MTIE1 0x00400000
+#define AS_MUMCR_MTIE2 0x00200000
+#define AS_MUMCR_MTIE3 0x00100000
+#define AS_MUMCR_MGIR0 0x00080000
+#define AS_MUMCR_MGIR1 0x00040000
+#define AS_MUMCR_MGIR2 0x00020000
+#define AS_MUMCR_MGIR3 0x00010000
+#define AS_MUMCR_MMUR 0x00000020
+#define AS_MUMCR_DHR 0x00000010
+#define AS_MUMCR_DNMI 0x00000008
+#define AS_MUMCR_MDF2 0x00000004
+#define AS_MUMCR_MDF1 0x00000002
+#define AS_MUMCR_MDF0 0x00000001
+
+#define BASE_NUM 1222
+
+#endif /* __MXC_MU_REG_H__ */
diff --git a/drivers/char/mxc_sdma_tty.c b/drivers/char/mxc_sdma_tty.c
new file mode 100644
index 000000000000..d0b59e4ff69e
--- /dev/null
+++ b/drivers/char/mxc_sdma_tty.c
@@ -0,0 +1,744 @@
+/*
+ * 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_sdma_tty.c
+ * @brief This file contains functions for SDMA TTY driver
+ *
+ * SDMA TTY driver is used for moving data between MCU and DSP using line discipline API
+ *
+ * @ingroup IPC
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/dma.h>
+#include <asm/mach/dma.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+#include <asm/system.h> /* save/local_irq_restore */
+#include <asm/uaccess.h> /* For copy_from_user */
+#include <linux/sched.h> /* For schedule */
+#include <linux/device.h>
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/circ_buf.h>
+
+#include <asm/arch/mxc_sdma_tty.h>
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#define SDMA_TTY_NORMAL_MAJOR 0
+
+#define WRITE_ROOM 512
+
+/*!
+ * This define returns the number of read SDMA channel
+ */
+#define READ_CHANNEL(line) (line < IPC_NB_CH_BIDIR) ? 2 * line + 1 : \
+ (line < (IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU)) ? \
+ (line + 3) : -1
+
+/*!
+ * This define returns the number of write SDMA channel
+ */
+#define WRITE_CHANNEL(line) (line < IPC_NB_CH_BIDIR) ? 2 * line + 2 : -1
+
+/*!
+ * This define returns 1 if the device is unidirectional
+ * DSP to MCU
+ */
+#define DSPMCU_DEVICE(line) line < (IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU) && \
+ line >= IPC_NB_CH_BIDIR
+
+/*!
+ * This holds tty driver definitions
+ */
+static struct tty_driver *sdma_tty_driver;
+
+/*!
+ * This holds the transmit tasklet
+ */
+static struct tasklet_struct sdma_tty_tasklet;
+
+static struct class *sdma_tty_class;
+
+/*!
+ * Structure containing sdma tty line status
+ */
+typedef struct {
+ struct tty_struct *tty; /*!< pointer to tty struct of the line */
+ int loopback_mode; /*!< Loopback mode: 0 - disable, 1 - enable */
+ struct circ_buf write_buf; /*!< Write buffer */
+ char *write_buf_phys; /*!< Write buffer dma pointer */
+ char *read_buf; /*!< Read buffer */
+ char *read_buf_phys; /*!< Read buffer dma pointer */
+ int chars_in_buffer; /*!< Number of characters in write buffer */
+ int sending; /*!< Sending flag */
+ int sending_count; /*!< Number of characters sent
+ in last write operation */
+ int f_mode; /*!< File mode (read/write) */
+} sdma_tty_struct;
+
+static sdma_tty_struct sdma_tty_data[IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU];
+
+static void sdma_tty_write_tasklet(unsigned long arg)
+{
+ int line, tx_num;
+ struct tty_struct *tty;
+ dma_request_t writechnl_request;
+ struct circ_buf *circ;
+
+ tty = (struct tty_struct *)arg;
+ line = tty->index;
+ DPRINTK("SDMA %s on line %d\n", __FUNCTION__, line);
+
+ if (sdma_tty_data[line].chars_in_buffer > 0) {
+ circ = &sdma_tty_data[line].write_buf;
+ tx_num = CIRC_CNT_TO_END(circ->head, circ->tail, WRITE_ROOM);
+ writechnl_request.sourceAddr =
+ sdma_tty_data[line].write_buf_phys + circ->tail;
+ writechnl_request.count = tx_num;
+ mxc_dma_set_config(WRITE_CHANNEL(line), &writechnl_request, 0);
+ mxc_dma_start(WRITE_CHANNEL(line));
+ } else {
+ sdma_tty_data[line].sending = 0;
+ }
+}
+
+/*!
+ * This function called on SDMA write channel interrupt.
+ *
+ * @param arg number of SDMA channel
+ */
+static void sdma_tty_write_callback(void *arg)
+{
+ dma_request_t sdma_read_request, sdma_write_request;
+ int line, count = 0;
+ struct tty_struct *tty;
+ struct circ_buf *circ;
+
+ tty = (struct tty_struct *)arg;
+ line = tty->index;
+
+ mxc_dma_get_config(WRITE_CHANNEL(line), &sdma_write_request, 0);
+
+ count = sdma_write_request.count;
+ circ = &sdma_tty_data[line].write_buf;
+
+ DPRINTK("SDMA %s on line %d count %d\n", __FUNCTION__, line, count);
+
+ if (sdma_tty_data[line].loopback_mode) {
+ sdma_tty_data[line].sending_count = count;
+
+ mxc_dma_get_config(READ_CHANNEL(line), &sdma_read_request, 0);
+ sdma_read_request.count = count;
+ sdma_read_request.destAddr = sdma_tty_data[line].read_buf_phys;
+
+ if (count <= tty_buffer_request_room(tty, count)) {
+ tty_flip_buffer_push(tty);
+ }
+
+ mxc_dma_set_config(READ_CHANNEL(line), &sdma_read_request, 0);
+ mxc_dma_start(READ_CHANNEL(line));
+ }
+ sdma_tty_data[line].chars_in_buffer -= count;
+ circ->tail = (circ->tail + count) & (WRITE_ROOM - 1);
+ DPRINTK("SDMA %s on head %x tail %x\n", __FUNCTION__, circ->head,
+ circ->tail);
+ wake_up_interruptible(&tty->write_wait);
+
+ if (sdma_tty_data[line].chars_in_buffer > 0) {
+ tasklet_schedule(&sdma_tty_tasklet);
+ } else {
+ sdma_tty_data[line].sending = 0;
+ }
+}
+
+/*!
+ * This function called on SDMA read channel interrupt.
+ *
+ * @param arg number of SDMA channel
+ */
+static void sdma_tty_read_callback(void *arg)
+{
+ dma_request_t read_request;
+ struct tty_struct *tty;
+ int line;
+ int count;
+
+ tty = (struct tty_struct *)arg;
+ line = tty->index;
+
+ if (sdma_tty_data[line].loopback_mode &&
+ sdma_tty_data[line].sending_count <= 0) {
+ return;
+ }
+
+ mxc_dma_get_config(READ_CHANNEL(line), &read_request, 0);
+
+ if (read_request.bd_done == 1) {
+ /* BD is not done yet */
+ return;
+ }
+
+ count = read_request.count;
+ /* Check for space availability in the TTY Flip buffer */
+ count = tty_buffer_request_room(tty, count);
+ if (!count) {
+ goto drop_data;
+ }
+
+ /* Set the real_raw flag to prevent processing on the received chars */
+ tty->real_raw = 1;
+ tty_insert_flip_string(tty, sdma_tty_data[line].read_buf, count);
+ tty_flip_buffer_push(tty);
+
+ wake_up_interruptible(&tty->read_wait);
+
+ drop_data:
+ if (!sdma_tty_data[line].loopback_mode) {
+ read_request.count = WRITE_ROOM;
+ read_request.destAddr = sdma_tty_data[line].read_buf_phys;
+
+ mxc_dma_set_config(READ_CHANNEL(line), &read_request, 0);
+ mxc_dma_start(READ_CHANNEL(line));
+ } else {
+ sdma_tty_data[line].sending_count = 0;
+ }
+
+ DPRINTK("Exit\n");
+}
+
+/*!
+ * This function changes loopback mode
+ *
+ * @param tty pointer to current tty line structure
+ * @param mode loopback mode: 0 - disable, 1 enable
+ * @param line tty line
+ * @return 0 on success, error code on fail
+ */
+static int sdma_tty_change_mode(struct tty_struct *tty, int mode, int line)
+{
+ dma_channel_params read_sdma_params, write_sdma_params;
+ dma_request_t sdma_read_request;
+ int res = 0;
+
+ if (!(mode == 0 || mode == 1)) {
+ printk(KERN_WARNING "Illegal loopback mode value\n");
+ return -EINVAL;
+ }
+
+ if ((mode == 1) && (!((sdma_tty_data[line].f_mode & FMODE_READ) &&
+ (sdma_tty_data[line].f_mode & FMODE_WRITE)))) {
+ printk(KERN_WARNING
+ "Loopback mode requires channel to be have read and write modes\n");
+ return -EINVAL;
+ }
+
+ /* Mode not changed */
+ if (sdma_tty_data[line].loopback_mode == mode) {
+ return 0;
+ }
+
+ sdma_tty_data[line].loopback_mode = mode;
+
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ mxc_dma_stop(READ_CHANNEL(line));
+ }
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ mxc_dma_stop(WRITE_CHANNEL(line));
+ }
+
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ /* SDMA read channel setup */
+ memset(&read_sdma_params, 0, sizeof(dma_channel_params));
+ read_sdma_params.peripheral_type = DSP;
+ read_sdma_params.transfer_type =
+ (mode == 0) ? dsp_2_emi : dsp_2_emi_loop;
+ read_sdma_params.event_id = 0;
+ read_sdma_params.callback = sdma_tty_read_callback;
+ read_sdma_params.arg = tty;
+ res =
+ mxc_dma_setup_channel(READ_CHANNEL(line),
+ &read_sdma_params);
+ if (res < 0) {
+ return res;
+ }
+ tty_flip_buffer_push(tty);
+
+ /* SDMA read request setup */
+ memset(&sdma_read_request, 0, sizeof(dma_request_t));
+ sdma_read_request.destAddr = sdma_tty_data[line].read_buf_phys;
+
+ mxc_dma_set_config(READ_CHANNEL(line), &sdma_read_request, 0);
+
+ if (!sdma_tty_data[line].loopback_mode) {
+ mxc_dma_start(READ_CHANNEL(line));
+ }
+
+ }
+
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ /* Write SDMA channel setup */
+ memset(&write_sdma_params, 0, sizeof(dma_channel_params));
+ write_sdma_params.peripheral_type = DSP;
+ write_sdma_params.transfer_type =
+ (mode == 0) ? emi_2_dsp : emi_2_dsp_loop;
+ write_sdma_params.event_id = 0;
+ write_sdma_params.callback = sdma_tty_write_callback;
+ write_sdma_params.arg = tty;
+ res =
+ mxc_dma_setup_channel(WRITE_CHANNEL(line),
+ &write_sdma_params);
+ if (res < 0) {
+ return res;
+ }
+ }
+
+ if (mode) {
+ /* Echo off to avoid infinite loop */
+ tty->termios->c_lflag = 0;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function opens tty line
+ *
+ * @param tty pointer to current tty line structure
+ * @param filp pointer to file structure
+ * @return 0 on success, error code on fail
+ */
+static int sdma_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ int channels[2];
+ int line;
+ int res;
+
+ line = tty->index;
+
+ if (DSPMCU_DEVICE(line) && (filp->f_mode & FMODE_WRITE)) {
+ printk(KERN_WARNING
+ "Error: Cannot open this channel for writing.\n");
+ return -EINVAL;
+ }
+
+ memset(&sdma_tty_data[line], 0, sizeof(sdma_tty_struct));
+
+ channels[0] = READ_CHANNEL(line);
+ channels[1] = WRITE_CHANNEL(line);
+
+ sdma_tty_data[line].tty = tty;
+ sdma_tty_data[line].f_mode = filp->f_mode;
+
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ res = mxc_request_dma(channels, "SDMA TTY");
+ if (res < 0) { // For read
+ printk(KERN_WARNING
+ "Error: SDMA DSP read channel busy\n");
+ goto read_dma_req_failed;
+ }
+ }
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ res = mxc_request_dma(channels + 1, "SDMA TTY");
+ if (res < 0) { // For write
+ printk(KERN_WARNING
+ "Error: SDMA DSP write channel busy\n");
+ goto write_dma_req_failed;
+ }
+ }
+
+ sdma_tty_data[line].chars_in_buffer = 0;
+ sdma_tty_data[line].sending = 0;
+ sdma_tty_data[line].loopback_mode = -1;
+
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ sdma_tty_data[line].write_buf.buf = sdma_malloc(WRITE_ROOM);
+ sdma_tty_data[line].write_buf.head = 0;
+ sdma_tty_data[line].write_buf.tail = 0;
+ sdma_tty_data[line].write_buf_phys =
+ (char *)sdma_virt_to_phys(sdma_tty_data[line].write_buf.
+ buf);
+ }
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ sdma_tty_data[line].read_buf = sdma_malloc(WRITE_ROOM);
+ sdma_tty_data[line].read_buf_phys =
+ (char *)sdma_virt_to_phys(sdma_tty_data[line].read_buf);
+ }
+
+ tty->low_latency = 1; // High rate
+
+ res = sdma_tty_change_mode(tty, DEFAULT_LOOPBACK_MODE, line);
+ if (res < 0) {
+ goto change_mode_failed;
+ }
+ tasklet_init(&sdma_tty_tasklet, sdma_tty_write_tasklet,
+ (unsigned long)tty);
+ DPRINTK("SDMA %s raw = %d real_raw = %d\n", __FUNCTION__, tty->raw,
+ tty->real_raw);
+
+ return 0;
+
+ change_mode_failed:
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ sdma_free(sdma_tty_data[line].write_buf.buf);
+ }
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ sdma_free(sdma_tty_data[line].read_buf);
+ }
+ write_dma_req_failed:
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ mxc_free_dma(READ_CHANNEL(line));
+ }
+ read_dma_req_failed:
+ return res;
+}
+
+/*!
+ * This function closes tty line
+ *
+ * @param tty pointer to current tty line structure
+ * @param filp pointer to file structure
+ */
+static void sdma_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ int line;
+
+ line = tty->index;
+
+ if (sdma_tty_data[line].f_mode & FMODE_READ) {
+ mxc_free_dma(READ_CHANNEL(line));
+ sdma_free(sdma_tty_data[line].read_buf);
+ }
+
+ if (sdma_tty_data[line].f_mode & FMODE_WRITE) {
+ mxc_free_dma(WRITE_CHANNEL(line));
+ sdma_free(sdma_tty_data[line].write_buf.buf);
+ sdma_tty_data[line].write_buf.buf = NULL;
+ }
+}
+
+/*!
+ * This function performs ioctl commands
+ *
+ * The driver supports 2 ioctls: TIOCLPBACK and TCGETS
+ * Other ioctls are supported by upper tty layers.
+ *
+ * @param tty pointer to current tty line structure
+ * @param file pointer to file structure
+ * @param cmd command
+ * @param arg pointer to argument
+ * @return 0 on success, error code on fail
+ */
+static int sdma_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int res;
+ int line;
+
+ line = tty->index;
+
+ switch (cmd) {
+ case TIOCLPBACK:
+ res = sdma_tty_change_mode(tty, *((int *)arg), line);
+
+ return res;
+ break;
+ case TCGETS:
+ if (copy_to_user((struct termios *)arg,
+ tty->termios, sizeof(struct termios))) {
+ return -EFAULT;
+ }
+ return (0);
+ break;
+ default:
+ /* printk(KERN_WARNING "Unsupported %d ioctl\n", cmd); */
+ break;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+/*!
+ * This function starts SDMA for sending data from write buffer
+ *
+ * @param tty pointer to current tty line structure
+ * @param count number of characters
+ * @return number of characters copied to write buffer
+ */
+static ssize_t sdma_tty_write_to_device(struct tty_struct *tty, int count)
+{
+ dma_request_t sdma_write_request;
+ int line;
+
+ line = tty->index;
+
+ DPRINTK("SDMA %s on line %d %d\n", __FUNCTION__, line, count);
+
+ memset(&sdma_write_request, 0, sizeof(dma_request_t));
+
+ sdma_tty_data[line].sending_count = count;
+
+ sdma_write_request.sourceAddr = sdma_tty_data[line].write_buf_phys +
+ sdma_tty_data[line].write_buf.tail;
+ sdma_write_request.count = count;
+
+ mxc_dma_set_config(WRITE_CHANNEL(line), &sdma_write_request, 0);
+
+ mxc_dma_start(WRITE_CHANNEL(line));
+
+ return count;
+}
+
+/*!
+ * This function flushes chars from write buffer
+ *
+ * @param tty pointer to current tty line structure
+ */
+static void sdma_tty_flush_chars(struct tty_struct *tty)
+{
+ int line, count = 0;
+ struct circ_buf *circ;
+
+ line = tty->index;
+ circ = &sdma_tty_data[line].write_buf;
+
+ if (sdma_tty_data[line].sending == 1) {
+ return;
+ }
+
+ if (sdma_tty_data[line].chars_in_buffer == 0) {
+ return;
+ }
+
+ sdma_tty_data[line].sending = 1;
+ count = CIRC_CNT_TO_END(circ->head, circ->tail, WRITE_ROOM);
+ sdma_tty_write_to_device(tty, count);
+}
+
+/*!
+ * This function writes characters to write buffer
+ *
+ * @param tty pointer to current tty line structure
+ * @param buf pointer to buffer
+ * @param count number of characters
+ * @return number of characters copied to write buffer
+ */
+static ssize_t sdma_tty_write(struct tty_struct *tty,
+ const unsigned char *buf, int count)
+{
+ int line;
+ int write_room, ret = 0;
+ struct circ_buf *circ;
+ line = tty->index;
+
+ DPRINTK("SDMA %s on line %d %d\n", __FUNCTION__, line, count);
+ circ = &sdma_tty_data[line].write_buf;
+
+ if (circ->head == circ->tail) {
+ circ->head = 0;
+ circ->tail = 0;
+ }
+
+ while (1) {
+ write_room =
+ CIRC_SPACE_TO_END(circ->head, circ->tail, WRITE_ROOM);
+ if (count < write_room) {
+ write_room = count;
+ }
+ if (write_room <= 0) {
+ break;
+ }
+
+ memcpy(circ->buf + circ->head, buf, write_room);
+ circ->head = (circ->head + write_room) & (WRITE_ROOM - 1);
+ buf += write_room;
+ count -= write_room;
+ ret += write_room;
+ }
+ DPRINTK("SDMA %s on head %x tail %x\n", __FUNCTION__, circ->head,
+ circ->tail);
+ sdma_tty_data[line].chars_in_buffer += ret;
+ sdma_tty_flush_chars(tty);
+
+ return ret;
+}
+
+/*!
+ * This function returns the number of characters in write buffer
+ *
+ * @param tty pointer to current tty line structure
+ * @return number of characters in write buffer
+ */
+static int sdma_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ int res;
+ int line;
+
+ line = tty->index;
+
+ res = sdma_tty_data[line].chars_in_buffer;
+
+ return res;
+}
+
+/*!
+ * This function returns how much room the tty driver has available in the write buffer
+ *
+ * @param tty pointer to current tty line structure
+ * @return how much room the tty driver has available in the write buffer
+ */
+static int sdma_tty_write_room(struct tty_struct *tty)
+{
+ int res;
+ int line;
+ struct circ_buf *circ;
+
+ line = tty->index;
+ circ = &sdma_tty_data[line].write_buf;
+
+ res = CIRC_SPACE(circ->head, circ->tail, WRITE_ROOM);
+
+ return res;
+}
+
+/*!
+ * TTY operation structure
+ */
+static struct tty_operations sdma_tty_ops = {
+ .open = sdma_tty_open,
+ .close = sdma_tty_close,
+ .write_room = sdma_tty_write_room,
+ .write = sdma_tty_write,
+ .flush_chars = sdma_tty_flush_chars,
+ .ioctl = sdma_tty_ioctl,
+ .chars_in_buffer = sdma_tty_chars_in_buffer,
+};
+
+/*!
+ * This function registers the tty driver
+ */
+static int __init sdma_tty_init(void)
+{
+ int error;
+ int dev_id, dev_number;
+ int dev_mode;
+ struct class_device *temp_class;
+
+ dev_number = IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU;
+
+ sdma_tty_driver = alloc_tty_driver(dev_number);
+
+ if (!sdma_tty_driver) {
+ return -ENOMEM;
+ }
+
+ sdma_tty_driver->owner = THIS_MODULE;
+ sdma_tty_driver->name = "mxc_sdma_tty";
+ sdma_tty_driver->name_base = 0;
+ sdma_tty_driver->major = SDMA_TTY_NORMAL_MAJOR;
+ sdma_tty_driver->minor_start = 0;
+ sdma_tty_driver->num = dev_number;
+ sdma_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ sdma_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ sdma_tty_driver->init_termios = tty_std_termios;
+ sdma_tty_driver->flags = TTY_DRIVER_DYNAMIC_DEV;
+ sdma_tty_driver->flags |= TTY_DRIVER_REAL_RAW; /* lpj */
+ tty_set_operations(sdma_tty_driver, &sdma_tty_ops);
+
+ if ((error = tty_register_driver(sdma_tty_driver))) {
+ printk(KERN_ERR "SDMA_TTY: Couldn't register SDMA_TTY driver,\
+ error = %d\n", error);
+ put_tty_driver(sdma_tty_driver);
+
+ return error;
+ }
+
+ sdma_tty_class = class_create(THIS_MODULE, "sdma");
+ if (IS_ERR(sdma_tty_class)) {
+ printk(KERN_ERR "Error creating sdma tty class.\n");
+ return PTR_ERR(sdma_tty_class);
+ }
+
+ dev_mode = S_IFCHR | S_IRUGO | S_IWUGO;
+ for (dev_id = 0; dev_id < dev_number; dev_id++) {
+ temp_class =
+ class_device_create(sdma_tty_class, NULL,
+ MKDEV(sdma_tty_driver->major,
+ dev_id), NULL, "sdma%u", dev_id);
+ if (IS_ERR(temp_class))
+ goto err_out;
+
+ if (dev_id == IPC_NB_CH_BIDIR) {
+ dev_mode = S_IFCHR | S_IRUGO;
+ }
+ if (dev_id == IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU) {
+ dev_mode = S_IFCHR | S_IWUGO;
+ }
+ }
+
+ printk("SDMA TTY Driver initialized\n");
+ return error;
+
+ err_out:
+ printk(KERN_ERR "Error creating sdma class or class device.\n");
+ for (dev_id = 0; dev_id < dev_number; dev_id++) {
+ class_device_destroy(sdma_tty_class, MKDEV
+ (sdma_tty_driver->major, dev_id));
+ }
+ class_destroy(sdma_tty_class);
+ return -1;
+}
+
+/*!
+ * This function unregisters the tty driver
+ */
+static void __exit sdma_tty_exit(void)
+{
+
+ int dev_id, dev_number;
+
+ dev_number = IPC_NB_CH_BIDIR + IPC_NB_CH_DSPMCU;
+
+ if (!sdma_tty_driver) {
+ for (dev_id = 0; dev_id < dev_number; dev_id++) {
+ class_device_destroy(sdma_tty_class,
+ MKDEV(sdma_tty_driver->major,
+ dev_id));
+ }
+ class_destroy(sdma_tty_class);
+ }
+
+ tty_unregister_driver(sdma_tty_driver);
+ put_tty_driver(sdma_tty_driver);
+}
+
+module_init(sdma_tty_init);
+module_exit(sdma_tty_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC SDMA TTY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/watchdog/mxc_wdt.c b/drivers/char/watchdog/mxc_wdt.c
new file mode 100644
index 000000000000..126b14357d5a
--- /dev/null
+++ b/drivers/char/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-2007 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 <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <asm/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(res->start);
+ 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(res->start);
+ }
+ 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(res->start);
+ mxc_wdt_ping(res->start);
+ }
+ 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/char/watchdog/mxc_wdt.h b/drivers/char/watchdog/mxc_wdt.h
new file mode 100644
index 000000000000..cd09b9acf99f
--- /dev/null
+++ b/drivers/char/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/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index c466c6cfc2e5..c5742313704f 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -311,6 +311,15 @@ 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_NFORCE2
tristate "Nvidia nForce2, nForce3 and nForce4"
depends on PCI
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 81d43c27cf93..c7dfff1cbd79 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
+obj-$(CONFIG_I2C_MXC) += mxc_i2c.o
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/busses/mxc_i2c.c b/drivers/i2c/busses/mxc_i2c.c
new file mode 100644
index 000000000000..677b252815cd
--- /dev/null
+++ b/drivers/i2c/busses/mxc_i2c.c
@@ -0,0 +1,762 @@
+/*
+ * 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_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/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;
+} mxc_i2c_device;
+
+/*!
+ * Boolean to indicate if data was transferred
+ */
+static bool transfer_done = false;
+
+/*!
+ * Boolean to indicate if we received an ACK for the data transmitted
+ */
+static bool tx_success = false;
+
+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)
+{
+ volatile unsigned int cr;
+
+ cr = readw(dev->membase + MXC_I2CR);
+ cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
+ writew(cr, dev->membase + MXC_I2CR);
+}
+
+/*!
+ * 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-- && !transfer_done) {
+ wait_event_interruptible_timeout(dev->wq,
+ transfer_done,
+ dev->adap.timeout);
+ }
+ transfer_done = false;
+
+ if (retry <= 0) {
+ /* Unable to send data */
+ printk(KERN_DEBUG "Data not transmitted\n");
+ return -1;
+ }
+
+ if (!tx_success) {
+ /* An ACK was not received for transmitted byte */
+ printk(KERN_DEBUG "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
+ */
+static void 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) {
+ printk(KERN_DEBUG "Could not grab Bus ownership\n");
+ }
+
+ /* 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);
+}
+
+/*!
+ * 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;
+ unsigned int addr_trans;
+
+ /*
+ * 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);
+ udelay(3);
+ 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;
+
+ if (dev->low_power) {
+ printk(KERN_ERR "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
+ */
+ if (sr & MXC_I2SR_IBB) {
+ mxc_i2c_module_dis(dev);
+ printk(KERN_DEBUG "Bus busy\n");
+ return -EREMOTEIO;
+ }
+ //gpio_i2c_active(dev->adap.id);
+ transfer_done = false;
+ 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
+ */
+ mxc_i2c_start(dev, &msgs[0]);
+ /* 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) {
+ printk(KERN_ERR "mxc_i2c_readbytes: fail.\n");
+ break;
+ }
+ } else {
+ /* Write the data */
+ ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num));
+ if (ret < 0) {
+ printk(KERN_ERR "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 - 1);
+}
+
+/*!
+ * 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) {
+ printk(KERN_DEBUG "Bus Arbitration lost\n");
+ } else {
+ /* Interrupt due byte transfer completion */
+ tx_success = true;
+ /* Check if RXAK is received in Transmit mode */
+ if ((cr & MXC_I2CR_MTX) && (sr & MXC_I2SR_RXAK)) {
+ tx_success = false;
+ }
+ 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
+ */
+ strcpy(mxc_i2c->adap.name, pdev->name);
+ mxc_i2c->adap.id = 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_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 = mxci2c_suspend,
+ .resume = 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_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 fb06555708a8..94e1958b952d 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -887,6 +887,13 @@ config BLK_DEV_IDE_BAST
Say Y here if you want to support the onboard IDE channels on the
Simtec BAST or the Thorcom VR1000
+config BLK_DEV_IDE_MXC
+ tristate "Freescale MXC IDE support"
+ depends on ARM && ( ARCH_MX3 || ARCH_MX27 || ARCH_MX33)
+ help
+ Say Y here if you want to support the IDE controller on the
+ Freescale iMX3 processor.
+
config ETRAX_IDE
bool "ETRAX IDE support"
depends on CRIS && BROKEN
@@ -1111,7 +1118,7 @@ config BLK_DEV_UMC8672
endif
config BLK_DEV_IDEDMA
- def_bool BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA
+ def_bool BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA || BLK_DEV_IDE_MXC
config IDE_ARCH_OBSOLETE_INIT
def_bool ALPHA || (ARM && !ARCH_L7200) || BLACKFIN || X86 || IA64 || M32R || MIPS || PARISC || PPC || (SUPERH64 && BLK_DEV_IDEPCI) || SPARC
diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile
index 6a78f0755f26..0b116dac847d 100644
--- a/drivers/ide/arm/Makefile
+++ b/drivers/ide/arm/Makefile
@@ -2,5 +2,6 @@
obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o
obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o
obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o
+obj-$(CONFIG_BLK_DEV_IDE_MXC) += mxc_ide.o
EXTRA_CFLAGS := -Idrivers/ide
diff --git a/drivers/ide/arm/mxc_ide.c b/drivers/ide/arm/mxc_ide.c
new file mode 100644
index 000000000000..142702245a5f
--- /dev/null
+++ b/drivers/ide/arm/mxc_ide.c
@@ -0,0 +1,1122 @@
+/*
+ * linux/drivers/ide/arm/mxc_ide.c
+ *
+ * Based on Simtec BAST IDE driver
+ * Copyright (c) 2003-2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Copyright 2004-2007 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 mxc_ide.c
+ *
+ * @brief ATA driver
+ *
+ * @ingroup ATA
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/clk.h>
+
+#include <asm/mach-types.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/hardware.h>
+#include <asm/arch/dma.h>
+#include "mxc_ide.h"
+
+extern void gpio_ata_active(void);
+extern void gpio_ata_inactive(void);
+static int mxc_ide_config_drive(ide_drive_t * drive, u8 xfer_mode);
+static void mxc_ide_dma_callback(void *arg, int error, unsigned int count);
+
+static struct clk *ata_clk;
+
+/* List of registered interfaces */
+static ide_hwif_t *ifs[1];
+
+/*
+ * 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])
+
+/*!
+ * Calculate values for the ATA bus timing registers and store
+ * them into the hardware.
+ *
+ * @param mode Selects PIO, MDMA or UDMA modes
+ *
+ * @param speed Specifies the sub-mode number
+ *
+ * @return EINVAL speed out of range, or illegal mode
+ */
+static int set_ata_bus_timing(int speed, enum ata_mode mode)
+{
+ /* get the bus clock cycle time, in ns */
+ int T = 1 * 1000 * 1000 * 1000 / clk_get_rate(ata_clk);
+ mxc_ide_time_cfg_t cfg0, cfg1, cfg2, cfg3, cfg4, cfg5;
+ /* every mode gets the same t_off and t_on */
+
+ GET_TIME_CFG(&cfg0, MXC_IDE_TIME_OFF);
+ cfg0.bytes.field1 = 3;
+ cfg0.bytes.field2 = 3;
+ SET_TIME_CFG(&cfg0, 3, MXC_IDE_TIME_OFF);
+
+ switch (mode) {
+ case PIO:
+ if (speed < 0 || speed >= NR_PIO_SPECS) {
+ return -EINVAL;
+ }
+ cfg0.bytes.field3 = (pio_specs[speed].t1 + T) / T;
+ cfg0.bytes.field4 = (pio_specs[speed].t2_8 + T) / T;
+
+ cfg1.bytes.field1 = (pio_specs[speed].t2_8 + T) / T;
+ cfg1.bytes.field2 = (pio_specs[speed].tA + T) / T + 2;
+ cfg1.bytes.field3 = 1;
+ cfg1.bytes.field4 = (pio_specs[speed].t4 + T) / T;
+
+ GET_TIME_CFG(&cfg2, MXC_IDE_TIME_9);
+ cfg2.bytes.field1 = (pio_specs[speed].t9 + T) / T;
+
+ SET_TIME_CFG(&cfg0, 0x0C, MXC_IDE_TIME_OFF);
+ SET_TIME_CFG(&cfg1, 0x0F, MXC_IDE_TIME_2r);
+ SET_TIME_CFG(&cfg2, 0x01, MXC_IDE_TIME_9);
+ break;
+ case MDMA:
+ if (speed < 0 || speed >= NR_MDMA_SPECS) {
+ return -EINVAL;
+ }
+ GET_TIME_CFG(&cfg2, MXC_IDE_TIME_9);
+ GET_TIME_CFG(&cfg3, MXC_IDE_TIME_K);
+
+ cfg2.bytes.field2 = (mdma_specs[speed].tM + T) / T;
+ cfg2.bytes.field3 = (mdma_specs[speed].tJNH + T) / T;
+ cfg2.bytes.field4 = (mdma_specs[speed].tD + T) / T;
+
+ cfg3.bytes.field1 = (mdma_specs[speed].tKW + T) / T;
+
+ SET_TIME_CFG(&cfg2, 0x0E, MXC_IDE_TIME_9);
+ SET_TIME_CFG(&cfg3, 0x01, MXC_IDE_TIME_K);
+ break;
+ case UDMA:
+ if (speed < 0 || speed >= NR_UDMA_SPECS) {
+ return -EINVAL;
+ }
+
+ GET_TIME_CFG(&cfg3, MXC_IDE_TIME_K);
+
+ cfg3.bytes.field2 = (udma_specs[speed].tACK + T) / T;
+ cfg3.bytes.field3 = (udma_specs[speed].tENV_min + T) / T;
+ cfg3.bytes.field4 = (udma_specs[speed].tRP + T) / T + 2;
+
+ cfg4.bytes.field1 = (udma_specs[speed].tZAH + T) / T;
+ cfg4.bytes.field2 = (udma_specs[speed].tMLI + T) / T;
+ cfg4.bytes.field3 = (udma_specs[speed].tDVH + T) / T + 1;
+ cfg4.bytes.field4 = (udma_specs[speed].tDZFS + T) / T;
+
+ cfg5.bytes.field1 = (udma_specs[speed].tDVS + T) / T;
+ cfg5.bytes.field2 = (udma_specs[speed].tCVH + T) / T;
+ cfg5.bytes.field3 = (udma_specs[speed].tSS + T) / T;
+ cfg5.bytes.field4 = (udma_specs[speed].tCYC + T) / T;
+
+ SET_TIME_CFG(&cfg3, 0x0E, MXC_IDE_TIME_K);
+ SET_TIME_CFG(&cfg4, 0x0F, MXC_IDE_TIME_ZAH);
+ SET_TIME_CFG(&cfg5, 0x0F, MXC_IDE_TIME_DVS);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * Placeholder for code to make any hardware tweaks
+ * necessary to select a drive. Currently we are
+ * not aware of any.
+ */
+static void mxc_ide_selectproc(ide_drive_t * drive)
+{
+}
+
+/*!
+ * Called to set the PIO mode.
+ *
+ * @param drive Specifies the drive
+ * @param pio Specifies the PIO mode number desired
+ */
+static void mxc_ide_tune(ide_drive_t * drive, u8 pio)
+{
+ set_ata_bus_timing(pio, PIO);
+}
+
+/*!
+ * Hardware-specific interrupt service routine for the ATA driver,
+ * called mainly just to dismiss the interrupt at the hardware, and
+ * to indicate whether there actually was an interrupt pending.
+ *
+ * The generic IDE related interrupt handling is done by the IDE layer.
+ */
+static int mxc_ide_ack_intr(struct hwif_s *hw)
+{
+ unsigned char status = ATA_RAW_READ(MXC_IDE_INTR_PENDING);
+ unsigned char enable = ATA_RAW_READ(MXC_IDE_INTR_ENABLE);
+
+ /*
+ * The only interrupts we can actually dismiss are the FIFO conditions.
+ * INTRQ comes from the bus, and must be dismissed according to IDE
+ * protocol, which will be done by the IDE layer, even when DMA
+ * is invovled (DMA can see it, but can't dismiss it).
+ */
+ ATA_RAW_WRITE(status, MXC_IDE_INTR_CLEAR);
+
+ if (status & enable & ~MXC_IDE_INTR_ATA_INTRQ2) {
+ printk(KERN_ERR "mxc_ide_ack_intr: unexpected interrupt, "
+ "status=0x%02X\n", status);
+ }
+
+ return status ? 1 : 0;
+}
+
+/*!
+ * Decodes the specified transfer mode and sets both timing and ultra modes
+ *
+ * @param drive Specifies the drive
+ *
+ * @param xfer_mode Specifies the desired transfer mode
+ *
+ * @return EINVAL Illegal mode specified
+ */
+static int mxc_ide_set_speed(ide_drive_t * drive, u8 xfer_mode)
+{
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) HWIF(drive)->hwif_data;
+
+ switch (xfer_mode) {
+ case XFER_UDMA_7:
+ case XFER_UDMA_6:
+ case XFER_UDMA_5:
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ priv->ultra = 1;
+ return set_ata_bus_timing(xfer_mode - XFER_UDMA_0, UDMA);
+ break;
+ case XFER_MW_DMA_2:
+ case XFER_MW_DMA_1:
+ case XFER_MW_DMA_0:
+ priv->ultra = 0;
+ return set_ata_bus_timing(xfer_mode - XFER_MW_DMA_0, MDMA);
+ break;
+ case XFER_PIO_4:
+ case XFER_PIO_3:
+ case XFER_PIO_2:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ return set_ata_bus_timing(xfer_mode - XFER_PIO_0, PIO);
+ break;
+ }
+
+ return -EINVAL;
+}
+
+/*!
+ * Called when the IDE layer is disabling DMA on a drive.
+ *
+ * @param drive Specifies the drive
+ */
+static void mxc_ide_dma_off_quietly(ide_drive_t * drive)
+{
+ drive->using_dma = 0;
+}
+
+/*!
+ * Called by the IDE layer when something goes wrong
+ *
+ * @param drive Specifies the drive
+ *
+ */
+static void mxc_ide_resetproc(ide_drive_t * drive)
+{
+ printk(KERN_INFO "%s: resetting ATA controller\n", __func__);
+
+ ATA_RAW_WRITE(0x00, MXC_IDE_ATA_CONTROL);
+ udelay(100);
+ ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B, MXC_IDE_ATA_CONTROL);
+ udelay(100);
+}
+
+/*!
+ * Turn on DMA for a drive.
+ *
+ * @param drive Specifies the drive
+ *
+ * @return 0 if successful
+ */
+static int mxc_ide_dma_on(ide_drive_t * drive)
+{
+ /* consult the list of known "bad" drives */
+ if (__ide_dma_bad_drive(drive))
+ return 1;
+
+ /*
+ * Go to UDMA3 mode. Beyond that you'll no doubt
+ * need an Ultra-100 cable (the 80 pin type with
+ * ground leads between all the signals)
+ */
+ printk(KERN_INFO "%s: enabling UDMA3 mode\n", drive->name);
+ mxc_ide_config_drive(drive, XFER_UDMA_3);
+ drive->using_dma = 1;
+
+ blk_queue_max_hw_segments(drive->queue, MXC_IDE_DMA_BD_NR);
+ blk_queue_max_hw_segments(drive->queue, MXC_IDE_DMA_BD_NR);
+ blk_queue_max_segment_size(drive->queue, MXC_IDE_DMA_BD_SIZE_MAX);
+
+ HWIF(drive)->dma_host_on(drive);
+ return 0;
+}
+
+/*!
+ * Turn on DMA if the drive can handle it.
+ *
+ * @param drive Specifies the drive
+ *
+ * @return 0 if successful
+ */
+static int mxc_ide_dma_check(ide_drive_t * drive)
+{
+ struct hd_driveid *id = drive->id;
+ if (id && (id->capability & 1)) {
+ /*
+ * Enable DMA on any drive that has
+ * UltraDMA (mode 0/1/2/3/4/5/6) enabled
+ */
+ if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f))
+ return HWIF(drive)->ide_dma_on(drive);
+ /*
+ * Enable DMA on any drive that has mode2 DMA
+ * (multi or single) enabled
+ */
+ if (id->field_valid & 2) /* regular DMA */
+ if ((id->dma_mword & 0x404) == 0x404 ||
+ (id->dma_1word & 0x404) == 0x404)
+ return HWIF(drive)->ide_dma_on(drive);
+
+ /* Consult the list of known "good" drives */
+ if (__ide_dma_good_drive(drive))
+ return HWIF(drive)->ide_dma_on(drive);
+ }
+ HWIF(drive)->dma_off_quietly(drive);
+ return 0;
+}
+
+/*!
+ * The DMA is done, and the drive is done. We'll check the BD array for
+ * errors, and unmap the scatter-gather list.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 0 means all is well, others means DMA signalled an error ,
+ */
+static int mxc_ide_dma_end(ide_drive_t * drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data;
+ int dma_stat = priv->dma_stat;
+
+ BUG_ON(drive->waiting_for_dma == 0);
+ drive->waiting_for_dma = 0;
+
+ /*
+ * We'll unmap the sg table regardless of status.
+ */
+ dma_unmap_sg(priv->dev, hwif->sg_table, hwif->sg_nents,
+ hwif->sg_dma_direction);
+
+ return dma_stat;
+}
+
+/*!
+ * The end-of-DMA interrupt handler
+ *
+ * @param drive Specifies the drive
+ *
+ * @return ide_stopped or ide_started
+ */
+static ide_startstop_t mxc_ide_dma_intr(ide_drive_t * drive)
+{
+ u8 stat, dma_stat;
+ struct request *rq = HWGROUP(drive)->rq;
+ ide_hwif_t *hwif = HWIF(drive);
+ u8 fifo_fill;
+
+ if (!rq)
+ return ide_stopped;
+
+ fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL);
+ BUG_ON(fifo_fill);
+
+ dma_stat = hwif->ide_dma_end(drive);
+ stat = hwif->INB(IDE_STATUS_REG); /* get drive status */
+ if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) {
+ if (dma_stat == MXC_DMA_DONE) {
+ ide_end_request(drive, 1, rq->nr_sectors);
+ return ide_stopped;
+ }
+ printk(KERN_ERR "%s: mxc_ide_dma_intr: bad DMA status (0x%x)\n",
+ drive->name, dma_stat);
+ }
+
+ return ide_error(drive, "mxc_ide_dma_intr", stat);
+}
+
+/*!
+ * Directs the IDE INTRQ signal to DMA or to the CPU
+ *
+ * @param hwif Specifies the IDE controller
+ *
+ * @param which \b INTRQ_DMA or \b INTRQ_MCU
+ *
+ */
+static void mxc_ide_set_intrq(ide_hwif_t * hwif, intrq_which_t which)
+{
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data;
+ unsigned char enable = ATA_RAW_READ(MXC_IDE_INTR_ENABLE);
+
+ switch (which) {
+ case INTRQ_DMA:
+ enable &= ~MXC_IDE_INTR_ATA_INTRQ2;
+ enable |= MXC_IDE_INTR_ATA_INTRQ1;
+ break;
+ case INTRQ_MCU:
+ enable &= ~MXC_IDE_INTR_ATA_INTRQ1;
+ enable |= MXC_IDE_INTR_ATA_INTRQ2;
+ break;
+ }
+ priv->enable = enable;
+
+ ATA_RAW_WRITE(enable, MXC_IDE_INTR_ENABLE);
+}
+
+/*!
+ * Helper routine to configure drive speed
+ *
+ * @param drive Specifies the drive
+ *
+ * @param xfer_mode Specifies the desired transfer mode
+ *
+ * @return 0 = success, non-zero otherwise
+ */
+static int mxc_ide_config_drive(ide_drive_t * drive, u8 xfer_mode)
+{
+ int err;
+ ide_hwif_t *hwif = HWIF(drive);
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) (hwif->hwif_data);
+ u8 prev = priv->enable;
+
+ mxc_ide_set_speed(drive, xfer_mode);
+
+ /*
+ * Work around an ADS hardware bug:
+ *
+ * OK, ide_config_drive_speed() is the right thing to call but it's
+ * going to leave an interrupt pending. We have to jump through hoops
+ * because the ADS board doesn't correctly terminate INTRQ. Instead
+ * of pulling it low, it pulls it high (asserted), so when the drive
+ * tri-states it, the MX31 sees it as an interrupt. So we'll disable
+ * the hardware interrupt here, and restore it a bit later, after we've
+ * selected the drive again and let it drive INTRQ low for a bit.
+ *
+ * On later ADS boards, when presumably the correct INTRQ termination
+ * is in place, these extra hoops won't be necessary, but they
+ * shouldn't hurt, either.
+ */
+ priv->enable = 0;
+ ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE);
+
+ err = ide_config_drive_speed(drive, xfer_mode);
+
+ if (ATA_RAW_READ(MXC_IDE_INTR_PENDING) &
+ (MXC_IDE_INTR_ATA_INTRQ2 | MXC_IDE_INTR_ATA_INTRQ1)) {
+ /*
+ * OK, the 'bad thing' is happening, so we'll clear nIEN to
+ * get the drive to drive INTRQ, then we'll read status to
+ * clear any pending interrupt. This sequence gets us by
+ * the spurious and stuck interrupt we see otherwise.
+ */
+ SELECT_DRIVE(drive);
+ udelay(2);
+ hwif->OUTB(0, IDE_CONTROL_REG);
+ udelay(2);
+ hwif->INB(IDE_STATUS_REG);
+ udelay(2);
+ }
+
+ priv->enable = prev;
+ ATA_RAW_WRITE(prev, MXC_IDE_INTR_ENABLE);
+
+ return err;
+}
+
+/*!
+ * Masks drive interrupts temporarily at the hardware level
+ *
+ * @param drive Specifies the drive
+ *
+ * @param mask 1 = disable interrupts, 0 = re-enable
+ *
+ */
+static void mxc_ide_maskproc(ide_drive_t * drive, int mask)
+{
+ mxc_ide_private_t *priv =
+ (mxc_ide_private_t *) (HWIF(drive)->hwif_data);
+ BUG_ON(!priv);
+
+ if (mask) {
+ ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE);
+ } else {
+ ATA_RAW_WRITE(priv->enable, MXC_IDE_INTR_ENABLE);
+ }
+}
+
+/*!
+ * DMA completion callback routine. This gets called after the DMA request
+ * has completed or aborted.All we need to do here is return ownership of
+ * the drive's INTRQ signal back to the CPU, which will immediately raise
+ * the interrupt to the IDE layer.
+ *
+ * @param arg The drive we're servicing
+ * @param error The error number of DMA transfer.
+ * @param count The transfered length.
+ */
+static void mxc_ide_dma_callback(void *arg, int error, unsigned int count)
+{
+ ide_hwif_t *hwif = HWIF((ide_drive_t *) arg);
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) (hwif->hwif_data);
+ unsigned long fifo_fill;
+
+ /*
+ * clean the fifo if the fill register is non-zero.
+ * If the fill register is non-zero, it is incorrect state.
+ */
+ fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL);
+ while (fifo_fill) {
+ ATA_RAW_READ(MXC_IDE_FIFO_DATA_32);
+ fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL);
+ }
+
+ priv->dma_stat = error;
+ /*
+ * Redirect ata_intrq back to us instead of the DMA.
+ */
+ mxc_ide_set_intrq(hwif, INTRQ_MCU);
+}
+
+/*!
+ * DMA set up. This is called once per DMA request to the drive. It delivers
+ * the scatter-gather list into the DMA channel and prepares both the ATA
+ * controller and the DMA engine for the DMA
+ * transfer.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 0 on success, non-zero otherwise
+ */
+static int mxc_ide_dma_setup(ide_drive_t * drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct scatterlist *sg = hwif->sg_table;
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data;
+ int dma_ultra = priv->ultra ? MXC_IDE_CTRL_DMA_ULTRA : 0;
+ int dma_mode = 0;
+ int chan;
+ u8 ata_control;
+ u8 fifo_fill;
+
+ BUG_ON(!rq);
+ BUG_ON(!priv);
+ BUG_ON(drive->waiting_for_dma);
+
+ /*Initialize the dma state */
+ priv->dma_stat = MXC_DMA_TRANSFER_ERROR;
+
+ /*
+ * Prepare the ATA controller for the DMA
+ */
+ if (rq_data_dir(rq)) {
+ chan = priv->dma_write_chan;
+ ata_control = MXC_IDE_CTRL_FIFO_RST_B |
+ MXC_IDE_CTRL_ATA_RST_B |
+ MXC_IDE_CTRL_FIFO_TX_EN |
+ MXC_IDE_CTRL_DMA_PENDING |
+ dma_ultra | MXC_IDE_CTRL_DMA_WRITE;
+
+ dma_mode = DMA_MODE_WRITE;
+ } else {
+ chan = priv->dma_read_chan;
+ ata_control = MXC_IDE_CTRL_FIFO_RST_B |
+ MXC_IDE_CTRL_ATA_RST_B |
+ MXC_IDE_CTRL_FIFO_RCV_EN |
+ MXC_IDE_CTRL_DMA_PENDING | dma_ultra;
+
+ dma_mode = DMA_MODE_READ;
+ }
+
+ /*
+ * Set up the DMA interrupt callback
+ */
+ mxc_dma_callback_set(chan, mxc_ide_dma_callback, (void *)drive);
+
+ /*
+ * If the ATA FIFO isn't empty, we shouldn't even be here
+ */
+ fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL);
+ BUG_ON(fifo_fill); // $$$ TODO: need better recovery here
+
+ ide_map_sg(drive, rq);
+
+ hwif->sg_dma_direction = rq_data_dir(rq) ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE;
+
+ hwif->sg_nents = dma_map_sg(priv->dev, sg, hwif->sg_nents,
+ hwif->sg_dma_direction);
+ BUG_ON(!hwif->sg_nents);
+ BUG_ON(hwif->sg_nents > MXC_IDE_DMA_BD_NR);
+
+ mxc_dma_sg_config(chan, sg, hwif->sg_nents, 0, dma_mode);
+
+ ATA_RAW_WRITE(ata_control, MXC_IDE_ATA_CONTROL);
+ ATA_RAW_WRITE(MXC_IDE_DMA_WATERMARK / 2, MXC_IDE_FIFO_ALARM);
+
+ /*
+ * Route ata_intrq to the DMA engine, and not to us.
+ */
+ mxc_ide_set_intrq(hwif, INTRQ_DMA);
+
+ /*
+ * The DMA and ATA controller are ready to go.
+ * mxc_ide_dma_start() will start the DMA transfer,
+ * and mxc_ide_dma_exec_cmd() will tickle the drive, which
+ * actually initiates the DMA transfer on the ATA bus.
+ * The ATA controller is DMA slave for both read and write.
+ */
+ BUG_ON(drive->waiting_for_dma);
+ drive->waiting_for_dma = 1;
+
+ return 0;
+}
+
+/*!
+ * DMA timeout notification. This gets called when the IDE layer above
+ * us times out waiting for a request.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 0 to attempt recovery, otherwise, an additional tick count
+ * to keep waiting
+ */
+static int mxc_ide_dma_timer_expiry(ide_drive_t * drive)
+{
+ printk(KERN_ERR "%s: fifo_fill=%d\n", drive->name,
+ readb(MXC_IDE_FIFO_FILL));
+
+ mxc_ide_resetproc(NULL);
+
+ if (drive->waiting_for_dma) {
+ HWIF(drive)->ide_dma_end(drive);
+ }
+
+ return 0;
+}
+
+/*!
+ * Called by the IDE layer to start a DMA request on the specified drive.
+ * The request has already been prepared by \b mxc_ide_dma_setup(). All
+ * we need to do is pass the command through while specifying our timeout
+ * handler.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @param cmd The IDE command for the drive
+ *
+ */
+static void mxc_ide_dma_exec_cmd(ide_drive_t * drive, u8 cmd)
+{
+ ide_execute_command(drive, cmd, mxc_ide_dma_intr, 2 * WAIT_CMD,
+ mxc_ide_dma_timer_expiry);
+}
+
+/*!
+ * Called by the IDE layer to start the DMA controller. The request has
+ * already been prepared by \b mxc_ide_dma_setup(). All we do here
+ * is tickle the DMA channel.
+ *
+ * @param drive The drive we're servicing
+ *
+ */
+static void mxc_ide_dma_start(ide_drive_t * drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ ide_hwif_t *hwif = HWIF(drive);
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data;
+ int chan = rq_data_dir(rq) ? priv->dma_write_chan : priv->dma_read_chan;
+
+ BUG_ON(chan < 0);
+
+ /*
+ * Tickle the DMA channel. This starts the channel, but it is likely
+ * that DMA will yield and go idle before the DMA request arrives from
+ * the drive. Nonetheless, at least the context will be hot.
+ */
+ mxc_dma_enable(chan);
+}
+
+/*!
+ * There is a race between the DMA interrupt and the timeout interrupt. This
+ * gets called during the IDE layer's timeout interrupt to see if the DMA
+ * interrupt has also occured or is pending.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 1 means there is a DMA interrupt pending, 0 otherwise
+ */
+static int mxc_ide_dma_test_irq(ide_drive_t * drive)
+{
+ unsigned char status = ATA_RAW_READ(MXC_IDE_INTR_PENDING);
+
+ /*
+ * We need to test the interrupt status without dismissing any.
+ */
+
+ return status & (MXC_IDE_INTR_ATA_INTRQ1
+ | MXC_IDE_INTR_ATA_INTRQ2) ? 1 : 0;
+}
+
+/*!
+ * Called to turn off DMA on this drive's controller, such as when a DMA error
+ * occured and the IDE layer wants to fail back to PIO mode. We won't do
+ * anything special, leaving the DMA channel allocated for future attempts.
+ *
+ * @param drive The drive we're servicing
+ */
+static void mxc_ide_dma_host_off(ide_drive_t * drive)
+{
+}
+
+/*!
+ * Called to turn on DMA on this drive's controller.
+ *
+ * @param drive The drive we're servicing
+ */
+static void mxc_ide_dma_host_on(ide_drive_t * drive)
+{
+}
+
+/*!
+ * Called for special handling on timeouts.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 0 if successful, non-zero otherwise
+ */
+static int mxc_ide_dma_timeout(ide_drive_t * drive)
+{
+ return 0;
+}
+
+/*!
+ * Called for special handling on lost irq's.
+ *
+ * @param drive The drive we're servicing
+ *
+ * @return 0 if successful, non-zero otherwise
+ */
+static int mxc_ide_dma_lost_irq(ide_drive_t * drive)
+{
+ return 0;
+}
+
+/*!
+ * Called once per controller to set up DMA
+ *
+ * @param hwif Specifies the IDE controller
+ *
+ */
+static void mxc_ide_dma_init(ide_hwif_t * hwif)
+{
+ mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data;
+
+ hwif->dmatable_cpu = NULL;
+ hwif->dmatable_dma = 0;
+ hwif->speedproc = mxc_ide_set_speed;
+ hwif->resetproc = mxc_ide_resetproc;
+ hwif->autodma = 0;
+
+ /*
+ * Allocate and setup the DMA channels
+ */
+ priv->dma_read_chan = mxc_dma_request(MXC_DMA_ATA_RX, "MXC ATA RX");
+ if (priv->dma_read_chan < 0) {
+ printk(KERN_ERR "%s: couldn't get RX DMA channel\n",
+ hwif->name);
+ goto err_out;
+ }
+
+ priv->dma_write_chan = mxc_dma_request(MXC_DMA_ATA_TX, "MXC ATA TX");
+ if (priv->dma_write_chan < 0) {
+ printk(KERN_ERR "%s: couldn't get TX DMA channel\n",
+ hwif->name);
+ goto err_out;
+ }
+
+ printk(KERN_INFO "%s: read chan=%d , write chan=%d \n",
+ hwif->name, priv->dma_read_chan, priv->dma_write_chan);
+
+ set_ata_bus_timing(0, UDMA);
+
+ /*
+ * All ready now
+ */
+ hwif->atapi_dma = 1;
+ hwif->ultra_mask = 0x7f;
+ hwif->mwdma_mask = 0x07;
+ hwif->swdma_mask = 0x07;
+ hwif->udma_four = 1;
+
+ hwif->dma_off_quietly = mxc_ide_dma_off_quietly;
+ hwif->ide_dma_on = mxc_ide_dma_on;
+ hwif->ide_dma_check = mxc_ide_dma_check;
+ hwif->dma_setup = mxc_ide_dma_setup;
+ hwif->dma_exec_cmd = mxc_ide_dma_exec_cmd;
+ hwif->dma_start = mxc_ide_dma_start;
+ hwif->ide_dma_end = mxc_ide_dma_end;
+ hwif->ide_dma_test_irq = mxc_ide_dma_test_irq;
+ hwif->dma_host_off = mxc_ide_dma_host_off;
+ hwif->dma_host_on = mxc_ide_dma_host_on;
+ hwif->ide_dma_timeout = mxc_ide_dma_timeout;
+ hwif->ide_dma_lostirq = mxc_ide_dma_lost_irq;
+
+ hwif->hwif_data = (void *)priv;
+ return;
+
+ err_out:
+ hwif->atapi_dma = 0;
+ if (priv->dma_read_chan >= 0)
+ mxc_dma_free(priv->dma_read_chan);
+ if (priv->dma_write_chan >= 0)
+ mxc_dma_free(priv->dma_write_chan);
+ kfree(priv);
+ return;
+}
+
+/*!
+ * MXC-specific IDE registration helper routine. Among other things,
+ * we tell the IDE layer where our standard IDE drive registers are,
+ * through the \b io_ports array. The IDE layer sends commands to and
+ * reads status directly from attached IDE drives through these drive
+ * registers.
+ *
+ * @param base Base address of memory mapped IDE standard drive registers
+ *
+ * @param aux Address of the auxilliary ATA control register
+ *
+ * @param irq IRQ number for our hardware
+ *
+ * @param hwifp Pointer to hwif structure to be updated by the IDE layer
+ *
+ * @param priv Pointer to private structure
+ *
+ * @return ENODEV if no drives are present, 0 otherwise
+ */
+static int __init
+mxc_ide_register(unsigned long base, unsigned int aux, int irq,
+ ide_hwif_t ** hwifp, mxc_ide_private_t * priv)
+{
+ int i = 0;
+ hw_regs_t hw;
+ ide_hwif_t *hwif = &ide_hwifs[0];
+ const int regsize = 4;
+
+ memset(&hw, 0, sizeof(hw));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw.io_ports[i] = (unsigned long)base;
+ base += regsize;
+ }
+ hw.io_ports[IDE_CONTROL_OFFSET] = aux;
+ hw.irq = irq;
+ hw.ack_intr = &mxc_ide_ack_intr;
+
+ *hwifp = hwif;
+ ide_register_hw(&hw, 0, hwifp);
+
+ hwif->selectproc = &mxc_ide_selectproc;
+ hwif->tuneproc = &mxc_ide_tune;
+ hwif->maskproc = &mxc_ide_maskproc;
+ hwif->hwif_data = (void *)priv;
+ mxc_ide_set_intrq(hwif, INTRQ_MCU);
+
+ /*
+ * The IDE layer will set hwif->present if we have devices attached,
+ * if we don't, discard the interface reset the ports.
+ */
+ if (!hwif->present) {
+ printk(KERN_INFO "ide%d: Bus empty, interface released.\n",
+ hwif->index);
+ for (i = IDE_DATA_OFFSET; i <= IDE_CONTROL_OFFSET; ++i)
+ hwif->io_ports[i] = 0;
+ hwif->chipset = ide_unknown;
+ hwif->noprobe = 1;
+ return -ENODEV;
+ }
+
+ mxc_ide_dma_init(hwif);
+
+ for (i = 0; i < MAX_DRIVES; i++) {
+ mxc_ide_dma_check(hwif->drives + i);
+ }
+
+ return 0;
+}
+
+/*!
+ * Driver initialization routine. Prepares the hardware for ATA activity.
+ */
+static int __init mxc_ide_init(void)
+{
+ int index = 0;
+ mxc_ide_private_t *priv;
+
+ printk(KERN_INFO
+ "MXC: IDE driver, (c) 2004-2006 Freescale Semiconductor\n");
+
+ ata_clk = clk_get(NULL, "ata_clk");
+ clk_enable(ata_clk);
+ ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B, MXC_IDE_ATA_CONTROL);
+
+ /* Select group B pins, and enable the interface */
+ gpio_ata_active();
+
+ /* Set initial timing and mode */
+
+ set_ata_bus_timing(4, PIO);
+
+ /* Reset the interface */
+
+ mxc_ide_resetproc(NULL);
+
+ /*
+ * Enable hardware interrupts.
+ * INTRQ2 goes to us, so we enable it here, but we'll need to ignore
+ * it when DMA is doing the transfer.
+ */
+ ATA_RAW_WRITE(MXC_IDE_INTR_ATA_INTRQ2, MXC_IDE_INTR_ENABLE);
+
+ /*
+ * Allocate a private structure
+ */
+ priv = (mxc_ide_private_t *) kmalloc(sizeof *priv, GFP_KERNEL);
+ if (priv == NULL) {
+ ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE);
+ gpio_ata_inactive();
+ return ENOMEM;
+ }
+
+ memset(priv, 0, sizeof *priv);
+ priv->dev = NULL; // dma_map_sg() ignores it anyway
+ priv->dma_read_chan = -1;
+ priv->dma_write_chan = -1;
+
+ /*
+ * Now register
+ */
+
+ index =
+ mxc_ide_register(IDE_ARM_IO, IDE_ARM_CTL, IDE_ARM_IRQ, &ifs[0],
+ priv);
+ if (index == -1) {
+ printk(KERN_ERR "Unable to register the MXC IDE driver\n");
+ ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE);
+ gpio_ata_inactive();
+ kfree(priv);
+ return ENODEV;
+ }
+#ifdef ATA_USE_IORDY
+ /* turn on IORDY protocol */
+
+ udelay(25);
+ ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B | MXC_IDE_CTRL_IORDY_EN,
+ MXC_IDE_ATA_CONTROL);
+#endif
+
+ return 0;
+}
+
+/*!
+ * Driver exit routine. Clean up.
+ */
+static void __exit mxc_ide_exit(void)
+{
+ ide_hwif_t *hwif = ifs[0];
+ mxc_ide_private_t *priv;
+
+ BUG_ON(!hwif);
+ priv = (mxc_ide_private_t *) hwif->hwif_data;
+ BUG_ON(!priv);
+
+ /*
+ * Unregister the interface at the IDE layer. This should shut
+ * down any drives and pending I/O.
+ */
+ ide_unregister(hwif->index);
+
+ /*
+ * Disable hardware interrupts.
+ */
+ ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE);
+
+ /*
+ * Deallocate DMA channels. Free's BDs and everything.
+ */
+ if (priv->dma_read_chan >= 0)
+ mxc_dma_free(priv->dma_read_chan);
+ if (priv->dma_write_chan >= 0)
+ mxc_dma_free(priv->dma_write_chan);
+
+ /*
+ * Turn off the clock
+ */
+ clk_disable(ata_clk);
+ clk_put(ata_clk);
+
+ /*
+ * Disable the interface
+ * Free the pins
+ */
+ gpio_ata_inactive();
+
+ /*
+ * Free the private structure.
+ */
+ kfree(priv);
+ hwif->hwif_data = NULL;
+
+}
+
+module_init(mxc_ide_init);
+module_exit(mxc_ide_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION
+ ("Freescale MXC IDE (based on Simtec BAST IDE driver by Ben Dooks <ben@simtec.co.uk>)");
diff --git a/drivers/ide/arm/mxc_ide.h b/drivers/ide/arm/mxc_ide.h
new file mode 100644
index 000000000000..cf1100b047f1
--- /dev/null
+++ b/drivers/ide/arm/mxc_ide.h
@@ -0,0 +1,195 @@
+/*
+ * 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_IDE_H_
+#define _MXC_IDE_H_
+
+/*!
+ * @defgroup ATA ATA/IDE Driver
+ */
+
+/*!
+ * @file mxc_ide.h
+ *
+ * @brief MXC ATA/IDE hardware register and bit definitions.
+ *
+ * @ingroup ATA
+ */
+
+#define IDE_ARM_IO IO_ADDRESS((ATA_BASE_ADDR + 0xA0) )
+#define IDE_ARM_CTL IO_ADDRESS((ATA_BASE_ADDR + 0xD8) )
+#define IDE_ARM_IRQ INT_ATA
+
+/*
+ * Interface control registers
+ */
+
+#define MXC_IDE_FIFO_DATA_32 IO_ADDRESS((ATA_BASE_ADDR + 0x18) )
+#define MXC_IDE_FIFO_DATA_16 IO_ADDRESS((ATA_BASE_ADDR + 0x1C) )
+#define MXC_IDE_FIFO_FILL IO_ADDRESS((ATA_BASE_ADDR + 0x20) )
+#define MXC_IDE_ATA_CONTROL IO_ADDRESS((ATA_BASE_ADDR + 0x24) )
+#define MXC_IDE_INTR_PENDING IO_ADDRESS((ATA_BASE_ADDR + 0x28) )
+#define MXC_IDE_INTR_ENABLE IO_ADDRESS((ATA_BASE_ADDR + 0x2C) )
+#define MXC_IDE_INTR_CLEAR IO_ADDRESS((ATA_BASE_ADDR + 0x30) )
+#define MXC_IDE_FIFO_ALARM IO_ADDRESS((ATA_BASE_ADDR + 0x34) )
+
+/*
+ * Control register bit definitions
+ */
+
+#define MXC_IDE_CTRL_FIFO_RST_B 0x80
+#define MXC_IDE_CTRL_ATA_RST_B 0x40
+#define MXC_IDE_CTRL_FIFO_TX_EN 0x20
+#define MXC_IDE_CTRL_FIFO_RCV_EN 0x10
+#define MXC_IDE_CTRL_DMA_PENDING 0x08
+#define MXC_IDE_CTRL_DMA_ULTRA 0x04
+#define MXC_IDE_CTRL_DMA_WRITE 0x02
+#define MXC_IDE_CTRL_IORDY_EN 0x01
+
+/*
+ * Interrupt registers bit definitions
+ */
+
+#define MXC_IDE_INTR_ATA_INTRQ1 0x80
+#define MXC_IDE_INTR_FIFO_UNDERFLOW 0x40
+#define MXC_IDE_INTR_FIFO_OVERFLOW 0x20
+#define MXC_IDE_INTR_CTRL_IDLE 0x10
+#define MXC_IDE_INTR_ATA_INTRQ2 0x08
+
+/*
+ * timing registers
+ */
+
+#define MXC_IDE_TIME_OFF IO_ADDRESS((ATA_BASE_ADDR + 0x00) )
+#define MXC_IDE_TIME_ON IO_ADDRESS((ATA_BASE_ADDR + 0x01) )
+#define MXC_IDE_TIME_1 IO_ADDRESS((ATA_BASE_ADDR + 0x02) )
+#define MXC_IDE_TIME_2w IO_ADDRESS((ATA_BASE_ADDR + 0x03) )
+
+#define MXC_IDE_TIME_2r IO_ADDRESS((ATA_BASE_ADDR + 0x04) )
+#define MXC_IDE_TIME_AX IO_ADDRESS((ATA_BASE_ADDR + 0x05) )
+#define MXC_IDE_TIME_PIO_RDX IO_ADDRESS((ATA_BASE_ADDR + 0x06) )
+#define MXC_IDE_TIME_4 IO_ADDRESS((ATA_BASE_ADDR + 0x07) )
+
+#define MXC_IDE_TIME_9 IO_ADDRESS((ATA_BASE_ADDR + 0x08) )
+#define MXC_IDE_TIME_M IO_ADDRESS((ATA_BASE_ADDR + 0x09) )
+#define MXC_IDE_TIME_JN IO_ADDRESS((ATA_BASE_ADDR + 0x0A) )
+#define MXC_IDE_TIME_D IO_ADDRESS((ATA_BASE_ADDR + 0x0B) )
+
+#define MXC_IDE_TIME_K IO_ADDRESS((ATA_BASE_ADDR + 0x0C) )
+#define MXC_IDE_TIME_ACK IO_ADDRESS((ATA_BASE_ADDR + 0x0D) )
+#define MXC_IDE_TIME_ENV IO_ADDRESS((ATA_BASE_ADDR + 0x0E) )
+#define MXC_IDE_TIME_RPX IO_ADDRESS((ATA_BASE_ADDR + 0x0F) )
+
+#define MXC_IDE_TIME_ZAH IO_ADDRESS((ATA_BASE_ADDR + 0x10) )
+#define MXC_IDE_TIME_MLIX IO_ADDRESS((ATA_BASE_ADDR + 0x11) )
+#define MXC_IDE_TIME_DVH IO_ADDRESS((ATA_BASE_ADDR + 0x12) )
+#define MXC_IDE_TIME_DZFS IO_ADDRESS((ATA_BASE_ADDR + 0x13) )
+
+#define MXC_IDE_TIME_DVS IO_ADDRESS((ATA_BASE_ADDR + 0x14) )
+#define MXC_IDE_TIME_CVH IO_ADDRESS((ATA_BASE_ADDR + 0x15) )
+#define MXC_IDE_TIME_SS IO_ADDRESS((ATA_BASE_ADDR + 0x16) )
+#define MXC_IDE_TIME_CYC IO_ADDRESS((ATA_BASE_ADDR + 0x17) )
+
+/*
+ * other facts
+ */
+#define MXC_IDE_FIFO_SIZE 64 /* DMA FIFO size in halfwords */
+#define MXC_IDE_DMA_BD_SIZE_MAX 0xFC00 /* max size of scatterlist segment */
+
+/*! Private data for the drive structure. */
+typedef struct {
+ struct device *dev; /*!< The device */
+ int dma_read_chan; /*!< DMA channel sdma api gave us for reads */
+ int dma_write_chan; /*!< DMA channel sdma api gave us for writes */
+ int ultra; /*!< Remember when we're in ultra mode */
+ int dma_stat; /*!< the state of DMA request */
+ u8 enable; /*!< Current hardware interrupt mask */
+} mxc_ide_private_t;
+
+/*! ATA transfer mode for set_ata_bus_timing() */
+enum ata_mode {
+ PIO, /*!< Specifies PIO mode */
+ MDMA, /*!< Specifies MDMA mode */
+ UDMA /*!< Specifies UDMA mode */
+};
+
+/*!
+ * The struct defines the interrupt type which will issued by interrupt singal.
+ */
+typedef enum {
+ /*! Enable ATA_INTRQ on the CPU */
+ INTRQ_MCU,
+
+ /*! Enable ATA_INTRQ on the DMA engine */
+ INTRQ_DMA
+} intrq_which_t;
+
+/*! defines the structure for timing configurations */
+
+/*!
+ * This structure defines the bits in the ATA TIME_CONFIGx
+ */
+typedef union {
+ unsigned long config; /* the 32bits fields in TIME_CONFIGx */
+ struct {
+ unsigned char field1; /* the 8bits field for 8bits accessing */
+ unsigned char field2; /* the 8bits field for 8bits accessing */
+ unsigned char field3; /* the 8bits field for 8bits accessing */
+ unsigned char field4; /* the 8bits field for 8bits accessing */
+ } bytes; /* the 8bits fields in TIME_CONFIGx */
+} mxc_ide_time_cfg_t;
+
+#ifdef MXC_ATA_USE_8_BIT_ACCESS /* use 8 bit access */
+
+/*!defines the macro for accessing the register */
+#define ATA_RAW_WRITE(v, addr) __raw_writeb(v, addr)
+#define ATA_RAW_READ(addr) __raw_readb(addr)
+
+/*! Get the configuration of TIME_CONFIG0 */
+#define GET_TIME_CFG(t, base)
+/*! Set the configuration of TIME_CONFIG0.
+ * And mask is the mask of valid fields.
+ * base is the start address of this cofniguration.
+ */
+#define SET_TIME_CFG(t, mask, base) \
+ { \
+ if ((mask) & 1) \
+ ATA_RAW_WRITE((t)->bytes.field1, (unsigned long)(base)); \
+ if ((mask) & 2) \
+ ATA_RAW_WRITE((t)->bytes.field2, (unsigned long)(base) + 1); \
+ if ((mask) & 4) \
+ ATA_RAW_WRITE((t)->bytes.field3, (unsigned long)(base) + 2); \
+ if ((mask) & 8) \
+ ATA_RAW_WRITE((t)->bytes.field4, (unsigned long)(base) + 3); \
+ }
+
+#else /*MXC_ATA_USE_8_BIT_ACCESS */
+
+/*!defines the macro for accessing the register */
+#define ATA_RAW_WRITE(v, addr) __raw_writel(v, addr)
+#define ATA_RAW_READ(addr) __raw_readl(addr)
+/*! Get the configuration of TIME_CONFIG0 */
+#define GET_TIME_CFG(t, base) \
+ { \
+ (t)->config = ATA_RAW_READ(base); \
+ }
+
+/*! Set the configuration of TIME_CONFIG0.
+ * And mask is ignored. base is the start address of this configuration.
+ */
+#define SET_TIME_CFG(t, mask, base) \
+ { \
+ ATA_RAW_WRITE((t)->config, base); \
+ }
+#endif /*MXC_ATA_USE_8_BIT_ACCESS */
+
+#endif /* !_MXC_IDE_H_ */
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 086d58c0ccbe..290254d23a29 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -247,6 +247,13 @@ config KEYBOARD_PXA27x
To compile this driver as a module, choose M here: the
module will be called pxa27x_keyboard.
+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
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index e97455fdcc83..b2b9abe5e645 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.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_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
diff --git a/drivers/input/keyboard/mxc_keyb.c b/drivers/input/keyboard/mxc_keyb.c
new file mode 100644
index 000000000000..cbc7141e7e39
--- /dev/null
+++ b/drivers/input/keyboard/mxc_keyb.c
@@ -0,0 +1,1024 @@
+/*
+ * 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_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
+
+#ifdef 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 <asm/arch/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 << MAXCOL) - 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 The function always returns 0.
+ */
+static int mxc_kpp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ del_timer(&kpp_dev.poll_timer);
+
+ 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);
+ }
+
+ init_timer(&kpp_dev.poll_timer);
+
+ 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", INT_KPP,
+ retval);
+ return -1;
+ }
+
+ /* 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)) {
+ free_irq(irq, MOD_NAME);
+ return -ENODEV;
+ }
+
+ mxckbd_dev = input_allocate_device();
+ if (!mxckbd_dev) {
+ printk(KERN_ERR
+ "mxckbd_dev: not enough memory for input device\n");
+ free_irq(irq, MOD_NAME);
+ return -ENOMEM;
+ }
+
+ mxckbd_dev->keycode = &mxckpd_keycodes;
+ mxckbd_dev->keycodesize = sizeof(unsigned char);
+ 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;
+
+ /* 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) {
+ free_irq(irq, MOD_NAME);
+ mxc_kpp_free_allocated();
+ return -1;
+ }
+
+ 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]) {
+ free_irq(irq, MOD_NAME);
+ mxc_kpp_free_allocated();
+ return -1;
+ }
+ }
+
+ 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) {
+ free_irq(irq, MOD_NAME);
+ mxc_kpp_free_allocated();
+ return -1;
+ }
+
+ __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);
+
+ input_register_device(mxckbd_dev);
+
+ /* 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;
+}
+
+/*!
+ * 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..853c0005e50e
--- /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 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/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 90e8e92dfe47..9b0692a19618 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -134,6 +134,18 @@ 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
+ 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_PENMOUNT
tristate "Penmount serial touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 35d4097df35a..364ae57f3e58 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MXC) += mxc_ts.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
diff --git a/drivers/input/touchscreen/mxc_ts.c b/drivers/input/touchscreen/mxc_ts.c
new file mode 100644
index 000000000000..a2c2a3bb6e09
--- /dev/null
+++ b/drivers/input/touchscreen/mxc_ts.c
@@ -0,0 +1,104 @@
+/*
+ * 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 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 <asm/arch/pmic_external.h>
+#include <asm/arch/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));
+ pmic_adc_get_touch_sample(&ts_sample, !wait);
+
+ 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)
+{
+ 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(EV_KEY) | BIT(EV_ABS);
+ mxc_inputdev->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
+ mxc_inputdev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+ input_register_device(mxc_inputdev);
+
+ 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/media/video/Kconfig b/drivers/media/video/Kconfig
index c9f14bfc8544..8820407ea87f 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -441,6 +441,33 @@ 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_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 b5a064163e03..30884338c398 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -55,6 +55,11 @@ obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_PLANB) += planb.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_IPU_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_EMMA_OUTPUT) += mxc/output/
+obj-$(CONFIG_VIDEO_MXC_OPL) += mxc/opl/
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..352871961fe3
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Kconfig
@@ -0,0 +1,90 @@
+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 && (MXC_CAMERA_MICRON111 || MXC_CAMERA_MC521DA)
+ select VIDEO_MXC_OPL
+ default y
+
+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"
+ default MXC_CAMERA_MICRON111
+ depends on (VIDEO_MXC_CAMERA && I2C_MXC)
+
+config MXC_CAMERA_MC521DA
+ tristate "Magnachip mc521da camera support"
+ depends on ((!MACH_I30030ADS) && (!MACH_MXC30030ADS))
+ ---help---
+ If you plan to use the mc521da Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_MICRON111
+ tristate "Micron mt9v111 camera support"
+ depends on ((!MACH_I30030ADS) && (!MACH_MXC30030ADS))
+ ---help---
+ If you plan to use the mt9v111 Camera with your MXC system, say Y here.
+
+config MXC_CAMERA_S5K3AAEX
+ tristate "Sumsung s5k3aaex camera support"
+ depends on ((MACH_I30030ADS || MACH_MXC30030ADS))
+ ---help---
+ If you plan to use the s5k3aaex Camera with your MXC system, say Y here.
+ Will be replaced by Magna hv7161.
+
+config MXC_CAMERA_HV7161
+ tristate "Magna Hv7161 camera support"
+ depends on ((MACH_I30030ADS || MACH_MXC30030ADS))
+ ---help---
+ If you plan to use the magna hv7161 Camera 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 && (MXC_CAMERA_MC521DA || MXC_CAMERA_MICRON111 || MXC_CAMERA_S5K3AAEX || MXC_CAMERA_HV7161))
+ 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 && (MXC_CAMERA_MC521DA || MXC_CAMERA_MICRON111 || MXC_CAMERA_S5K3AAEX || MXC_CAMERA_HV7161))
+ 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 && (MXC_CAMERA_MC521DA || MXC_CAMERA_MICRON111 || MXC_CAMERA_S5K3AAEX || MXC_CAMERA_HV7161))
+ default y
+ ---help---
+ Use case PRP_ENC:
+ Preprocessing image from smart sensor for encoder.
+ CSI -> IC (PRP ENC) -> 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..4e7c903a4896
--- /dev/null
+++ b/drivers/media/video/mxc/capture/Makefile
@@ -0,0 +1,21 @@
+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
+endif
+
+mx27_capture-objs := mx27_prphw.o mx27_prpsw.o mx27_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
+
+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
diff --git a/drivers/media/video/mxc/capture/hv7161.c b/drivers/media/video/mxc/capture/hv7161.c
new file mode 100644
index 000000000000..2ba0f92776ec
--- /dev/null
+++ b/drivers/media/video/mxc/capture/hv7161.c
@@ -0,0 +1,434 @@
+/*
+ * 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 hv7161.c
+ *
+ * @brief hv7161 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 "asm/arch/mxc_i2c.h"
+#include "hv7161.h"
+#include "mxc_v4l2_capture.h"
+
+#define HV7161_TERM 0xFF
+
+static sensor_interface *interface_param = NULL;
+static int reset_frame_rate = 30;
+
+static hv7161_image_format format[2] = {
+ {
+ .index = 0,
+ .width = 1280,
+ .height = 960,
+ },
+ {
+ .index = 1,
+ .width = 640,
+ .height = 480,
+ },
+};
+
+const static struct hv7161_reg hv7161_common[] = {
+ {0x31, 0x20}, {0x32, 0x3}, {0xee, 0x3},
+ {HV7161_TERM, HV7161_TERM}
+};
+
+static int hv7161_attach(struct i2c_adapter *adapter);
+static int hv7161_detach(struct i2c_client *client);
+
+static struct i2c_driver hv7161_i2c_driver = {
+ .owner = THIS_MODULE,
+ .name = "HV7161 Client",
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = hv7161_attach,
+ .detach_client = hv7161_detach,
+};
+
+static struct i2c_client hv7161_i2c_client = {
+ .name = "hv7161 I2C dev",
+ .id = 1,
+ .addr = HV7161_I2C_ADDRESS,
+ .driver = &hv7161_i2c_driver,
+};
+
+extern void gpio_sensor_setup(void);
+extern void gpio_sensor_reset(bool flag);
+extern void gpio_sensor_suspend(bool flag);
+
+/*
+ * Function definitions
+ */
+static int hv7161_i2c_client_xfer(unsigned int addr, char *reg,
+ int reg_len, char *buf, int num,
+ int tran_flag)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = addr;
+ msg[0].len = reg_len;
+ msg[0].buf = reg;
+ msg[0].flags = tran_flag;
+ msg[0].flags &= ~I2C_M_RD;
+
+ msg[1].addr = addr;
+ msg[1].len = num;
+ msg[1].buf = buf;
+ msg[1].flags = tran_flag;
+
+ if (tran_flag & MXC_I2C_FLAG_READ) {
+ msg[1].flags |= I2C_M_RD;
+ } else {
+ msg[1].flags &= ~I2C_M_RD;
+ }
+
+ ret = i2c_transfer(hv7161_i2c_client.adapter, msg, 2);
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+static int hv7161_read_reg(u8 * reg, u8 * val)
+{
+ return hv7161_i2c_client_xfer(HV7161_I2C_ADDRESS, reg, 1, val, 1,
+ MXC_I2C_FLAG_READ);
+}
+
+static int hv7161_write_reg(u8 reg, u8 val)
+{
+ u8 temp1, temp2;
+ temp1 = reg;
+ temp2 = val;
+ return hv7161_i2c_client_xfer(HV7161_I2C_ADDRESS, &temp1, 1, &temp2,
+ 1, 0);
+}
+
+static int hv7161_write_regs(const struct hv7161_reg reglist[])
+{
+ int err;
+ const struct hv7161_reg *next = reglist;
+
+ while (!((next->reg == HV7161_TERM) && (next->val == HV7161_TERM))) {
+ err = hv7161_write_reg(next->reg, next->val);
+ if (err) {
+ return err;
+ }
+ next++;
+ }
+ return 0;
+}
+
+/*!
+ * hv7161 sensor downscale function
+ * @param downscale bool
+ * @return Error code indicating success or failure
+ */
+static u8 hv7161_sensor_downscale(bool downscale)
+{
+ u8 error = 0;
+ u8 reg, data;
+
+ if (downscale == true) {
+ reg = 0x1;
+ data = 0x16;
+ hv7161_write_reg(reg, data);
+ } else {
+ reg = 0x1;
+ data = 0x13;
+ hv7161_write_reg(reg, data);
+ }
+
+ hv7161_write_regs(hv7161_common);
+
+ return error;
+}
+
+/*!
+ * hv7161 sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void hv7161_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 = 0x1;
+ param->Vsync_pol = 0x1;
+ param->Hsync_pol = 0x1;
+ param->width = width - 1;
+ param->height = height - 1;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+}
+
+/*!
+ * hv7161 sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+static sensor_interface *hv7161_config(int *frame_rate, int high_quality)
+{
+ u8 reg, data;
+ int num_clock_per_row;
+ u16 h_blank;
+ int max_rate = 0;
+ int index = 1;
+
+ if (high_quality == 1)
+ index = 0;
+
+ hv7161_interface(interface_param, format[index].width,
+ format[index].height);
+
+ if (index == 0) {
+ pr_info("SXGA\n");
+ hv7161_sensor_downscale(false);
+ } else {
+ pr_info("VGA\n");
+ hv7161_sensor_downscale(true);
+ }
+
+ num_clock_per_row = format[0].width + 208;
+ max_rate = interface_param->mclk / (num_clock_per_row *
+ (format[0].height + 8));
+
+ if ((*frame_rate > max_rate) || (*frame_rate == 0)) {
+ *frame_rate = max_rate;
+ }
+
+ num_clock_per_row = interface_param->mclk / *frame_rate;
+ num_clock_per_row /= format[0].height + 8;
+ h_blank = num_clock_per_row - format[0].width;
+ reg = 0x11;
+ data = (u8) (h_blank & 0xff);
+ hv7161_write_reg(reg, data);
+ reg = 0x10;
+ data = (u8) ((h_blank >> 8) & 0xff);
+ hv7161_write_reg(reg, data);
+
+ reset_frame_rate = *frame_rate;
+
+ return interface_param;
+}
+
+/*!
+ * hv7161 sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void
+hv7161_set_color(int bright, int saturation, int red, int green, int blue)
+{
+ u8 reg;
+ u8 data;
+
+ // set Brightness
+ reg = 0x5b;
+ data = (u8) bright;
+ hv7161_write_reg(reg, data);
+ // set Saturation
+ reg = 0x5c;
+ data = (u8) saturation;
+ hv7161_write_reg(reg, data);
+ // set Red
+ reg = 0x14;
+ data = (u8) red;
+ hv7161_write_reg(reg, data);
+ // set Green
+ reg = 0x15;
+ data = (u8) green;
+ hv7161_write_reg(reg, data);
+ // set Blue
+ reg = 0x16;
+ data = (u8) blue;
+ hv7161_write_reg(reg, data);
+}
+
+/*!
+ * hv7161 sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void
+hv7161_get_color(int *bright, int *saturation, int *red, int *green, int *blue)
+{
+ u8 reg[1];
+ u8 *pdata;
+
+ // get Brightness
+ reg[0] = 0x5b;
+ pdata = (u8 *) bright;
+ hv7161_read_reg(reg, pdata);
+ // get saturation
+ reg[0] = 0x5c;
+ pdata = (u8 *) saturation;
+ hv7161_read_reg(reg, pdata);
+ // get Red
+ reg[0] = 0x14;
+ pdata = (u8 *) red;
+ hv7161_read_reg(reg, pdata);
+ // get Green
+ reg[0] = 0x15;
+ pdata = (u8 *) red;
+ hv7161_read_reg(reg, pdata);
+ // get Blue
+ reg[0] = 0x16;
+ pdata = (u8 *) blue;
+ hv7161_read_reg(reg, pdata);
+}
+
+/*!
+ * hv7161 Reset function
+ *
+ * @return None
+ */
+static sensor_interface *hv7161_reset(void)
+{
+ set_mclk_rate(&interface_param->mclk);
+
+ /* Reset for at least 4 cycles */
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(10);
+
+ hv7161_config(&reset_frame_rate, 0);
+ return interface_param;
+}
+
+struct camera_sensor camera_sensor_if = {
+ set_color:hv7161_set_color,
+ get_color:hv7161_get_color,
+ config:hv7161_config,
+ reset:hv7161_reset,
+};
+
+/*!
+ * hv7161 I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int hv7161_attach(struct i2c_adapter *adapter)
+{
+ if (strcmp(adapter->name, MXC_ADAPTER_NAME) != 0) {
+ printk(KERN_ERR "hv7161_attach: %s\n", adapter->name);
+ return -1;
+ }
+
+ hv7161_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&hv7161_i2c_client)) {
+ hv7161_i2c_client.adapter = NULL;
+ printk(KERN_ERR "hv7161_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "hv7161_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ gpio_sensor_setup();
+
+ gpio_sensor_suspend(false);
+
+ interface_param->mclk = 0x2a00000;
+
+ return 0;
+}
+
+/*!
+ * hv7161 I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int hv7161_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!hv7161_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&hv7161_i2c_client);
+ hv7161_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+/*!
+ * hv7161 init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int hv7161_init(void)
+{
+ u8 err;
+
+ err = i2c_add_driver(&hv7161_i2c_driver);
+
+ return err;
+}
+
+/*!
+ * hv7161 cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit hv7161_clean(void)
+{
+ i2c_del_driver(&hv7161_i2c_driver);
+}
+
+module_init(hv7161_init);
+module_exit(hv7161_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("hv7161 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/hv7161.h b/drivers/media/video/mxc/capture/hv7161.h
new file mode 100644
index 000000000000..26c5bd60cdd3
--- /dev/null
+++ b/drivers/media/video/mxc/capture/hv7161.h
@@ -0,0 +1,38 @@
+/*
+ * 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 hv7161.h
+ *
+ * @brief HV7161 Camera Header file
+ *
+ * @ingroup Camera
+ */
+
+#ifndef __HV7161_H__
+#define __HV7161_H__
+
+#define HV7161_I2C_ADDRESS 0x11
+
+typedef struct {
+ u8 index;
+ u16 width;
+ u16 height;
+} hv7161_image_format;
+
+struct hv7161_reg {
+ u8 reg;
+ u8 val;
+};
+
+#endif /* __HV7161_H__ */
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..31fe635c0dd9
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_enc.c
@@ -0,0 +1,438 @@
+/*
+ * 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 ipu_prp_enc.c
+ *
+ * @brief IPU Use case for PRP-ENC
+ *
+ * @ingroup IPU
+ */
+
+#include "mxc_v4l2_capture.h"
+#include <asm/arch/ipu.h>
+#include "ipu_prp_sw.h"
+#include <linux/dma-mapping.h>
+
+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;
+
+ if (!cam) {
+ printk(KERN_ERR "cam private is NULL\n");
+ return -ENXIO;
+ }
+
+ ipu_csi_get_window_size(&enc.csi_prp_enc_mem.in_width,
+ &enc.csi_prp_enc_mem.in_height);
+ 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;
+ 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_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(CSI_MCLK_ENC, 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;
+
+ 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(CSI_MCLK_ENC, 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..1aa53e69b477
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_sw.h
@@ -0,0 +1,36 @@
+/*
+ * 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 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 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..56a82fba71a8
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_adc.c
@@ -0,0 +1,601 @@
+/*
+ * 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 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 <asm/arch/mxcfb.h>
+#include <asm/arch/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->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(CSI_MCLK_VF, 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->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->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(CSI_MCLK_VF, 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(CSI_MCLK_VF, 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->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->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->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(CSI_MCLK_VF, false, false);
+ }
+#ifndef CONFIG_MXC_IPU_PRP_VF_SDC
+ else if (cam->rotation == IPU_ROTATE_NONE) {
+ ipu_disable_channel(CSI_PRP_VF_ADC, false);
+ ipu_uninit_channel(CSI_PRP_VF_ADC);
+ ipu_csi_enable_mclk(CSI_MCLK_VF, 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(CSI_MCLK_VF, 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..def68818c693
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc.c
@@ -0,0 +1,462 @@
+/*
+ * 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 ipu_prp_vf_sdc.c
+ *
+ * @brief IPU Use case for PRP-VF
+ *
+ * @ingroup IPU
+ */
+
+#include "mxc_v4l2_capture.h"
+#include <asm/arch/ipu.h>
+#include "ipu_prp_sw.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;
+ u32 format = IPU_PIX_FMT_RGB565;
+ u32 size = 2;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "private is NULL\n");
+ return -EIO;
+ }
+
+ if (cam->overlay_active == true) {
+ pr_debug("already started.\n");
+ return 0;
+ }
+
+ 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);
+ 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;
+ if (cam->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(CSI_MCLK_VF, 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] = 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_3;
+ }
+ cam->vf_bufs_size[1] = 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->rotation >= IPU_ROTATE_90_RIGHT) {
+ 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],
+ (dma_addr_t) cam->rot_vf_bufs[0]);
+ cam->rot_vf_bufs_vaddr[0] = NULL;
+ cam->rot_vf_bufs[0] = 0;
+ }
+ if (cam->rot_vf_bufs[1]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[1],
+ cam->rot_vf_bufs_vaddr[1],
+ (dma_addr_t) cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_vf_bufs[1] = 0;
+ }
+ 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 "alloc 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
+ [0],
+ &cam->
+ rot_vf_bufs
+ [1],
+ GFP_DMA |
+ GFP_KERNEL);
+ if (cam->rot_vf_bufs_vaddr[1] == NULL) {
+ printk(KERN_ERR "alloc rot_vf_bufs.\n");
+ err = -ENOMEM;
+ goto out_3;
+ }
+ pr_debug("rot_vf_bufs %x %x\n", cam->rot_vf_bufs[0],
+ cam->rot_vf_bufs[1]);
+
+ 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->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_3;
+ }
+
+ 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 "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_init_channel(MEM_SDC_FG, NULL);
+ if (err != 0)
+ goto out_2;
+
+ ipu_sdc_set_window_pos(MEM_SDC_FG, cam->win.w.left,
+ cam->win.w.top);
+
+ err = ipu_init_channel_buffer(MEM_SDC_FG, 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 SDC FG buffer\n");
+ goto out_2;
+ }
+
+ err = ipu_link_channels(MEM_ROT_VF_MEM, MEM_SDC_FG);
+ if (err < 0) {
+ printk(KERN_ERR
+ "Error link MEM_ROT_VF_MEM-MEM_SDC_FG\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_ROT_VF_MEM);
+ ipu_enable_channel(MEM_SDC_FG);
+
+ 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->rotation,
+ 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_4;
+ }
+ err = ipu_init_channel(MEM_SDC_FG, NULL);
+ if (err != 0)
+ goto out_3;
+
+ ipu_sdc_set_window_pos(MEM_SDC_FG, cam->win.w.left,
+ cam->win.w.top);
+ err = ipu_init_channel_buffer(MEM_SDC_FG,
+ IPU_INPUT_BUFFER, format,
+ cam->win.w.width,
+ cam->win.w.height,
+ cam->win.w.width, IPU_ROTATE_NONE,
+ cam->vf_bufs[0], cam->vf_bufs[1],
+ 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error initializing SDC FG buffer\n");
+ goto out_1;
+ }
+
+ err = ipu_link_channels(CSI_PRP_VF_MEM, MEM_SDC_FG);
+ if (err < 0) {
+ printk(KERN_ERR "Error linking ipu channels\n");
+ goto out_1;
+ }
+
+ ipu_enable_channel(CSI_PRP_VF_MEM);
+ ipu_enable_channel(MEM_SDC_FG);
+
+ 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(MEM_SDC_FG);
+ out_2:
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ 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],
+ (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;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ (dma_addr_t) 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],
+ (dma_addr_t) 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;
+ int err = 0;
+
+ if (cam->overlay_active == false)
+ return 0;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_ROT_VF_MEM);
+ ipu_unlink_channels(MEM_ROT_VF_MEM, MEM_SDC_FG);
+ } else {
+ ipu_unlink_channels(CSI_PRP_VF_MEM, MEM_SDC_FG);
+ }
+
+ ipu_disable_channel(MEM_SDC_FG, true);
+ ipu_disable_channel(CSI_PRP_VF_MEM, true);
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT) {
+ ipu_disable_channel(MEM_ROT_VF_MEM, true);
+ ipu_uninit_channel(MEM_ROT_VF_MEM);
+ }
+ ipu_uninit_channel(MEM_SDC_FG);
+ ipu_uninit_channel(CSI_PRP_VF_MEM);
+
+ ipu_csi_enable_mclk(CSI_MCLK_VF, 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;
+ }
+ if (cam->rot_vf_bufs_vaddr[0]) {
+ dma_free_coherent(0, cam->rot_vf_buf_size[0],
+ cam->rot_vf_bufs_vaddr[0],
+ (dma_addr_t) 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],
+ (dma_addr_t) cam->rot_vf_bufs[1]);
+ cam->rot_vf_bufs_vaddr[1] = NULL;
+ cam->rot_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..009cc2687d66
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_prp_vf_sdc_bg.c
@@ -0,0 +1,410 @@
+/*
+ * 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 ipu_prp_vf_sdc_bg.c
+ *
+ * @brief IPU Use case for PRP-VF back-ground
+ *
+ * @ingroup IPU
+ */
+#include <linux/fb.h>
+#include "mxc_v4l2_capture.h"
+#include <asm/arch/ipu.h>
+#include "ipu_prp_sw.h"
+#include <linux/dma-mapping.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 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) {
+ size = 3;
+ pr_info("BGR24\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_RGB565) {
+ size = 2;
+ pr_info("RGB565\n");
+ } else if (cam->v4l2_fb.fmt.pixelformat == IPU_PIX_FMT_BGR32) {
+ 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);
+ 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;
+ if (cam->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(CSI_MCLK_VF, 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->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->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,
+ 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,
+ IPU_ROTATE_NONE, offset, 0, 0, 0);
+ if (err != 0) {
+ printk(KERN_ERR "Error MEM_ROT_VF_MEM output buffer\n");
+ goto out_2;
+ }
+ }
+
+ 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;
+ }
+
+ err = ipu_request_irq(IPU_IRQ_SDC_BG_OUT_EOF, prpvf_sdc_vsync_callback,
+ 0, "Mxc Camera", NULL);
+ if (err != 0) {
+ printk(KERN_ERR
+ "Error registering IPU_IRQ_SDC_BG_OUT_EOF 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_SDC_BG_OUT_EOF, 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(CSI_MCLK_VF, 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..ccbe6712f1db
--- /dev/null
+++ b/drivers/media/video/mxc/capture/ipu_still.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
+ */
+
+/*!
+ * @file ipu_still.c
+ *
+ * @brief IPU Use case for still image capture
+ *
+ * @ingroup IPU
+ */
+
+#include <asm/semaphore.h>
+#include "mxc_v4l2_capture.h"
+#include <asm/arch/ipu.h>
+#include "ipu_prp_sw.h"
+
+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;
+}
+
+/*!
+ * 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;
+
+ 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;
+
+ 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;
+ }
+
+ err = ipu_init_channel(CSI_MEM, NULL);
+ if (err != 0)
+ return err;
+ ipu_csi_enable_mclk(CSI_MCLK_RAW, true, true);
+
+ 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;
+
+ 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;
+ 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;
+ }
+
+ 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;
+
+ ipu_free_irq(IPU_IRQ_SENSOR_EOF, NULL);
+ ipu_free_irq(IPU_IRQ_SENSOR_OUT_EOF, cam);
+
+ ipu_disable_channel(CSI_MEM, true);
+ ipu_uninit_channel(CSI_MEM);
+ ipu_csi_enable_mclk(CSI_MCLK_RAW, 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..a3bcb83a1bff
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mc521da.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright 2006-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 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 <asm/arch/mxc_i2c.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,
+};
+
+/*
+ * Function definitions
+ */
+static int mc521da_i2c_client_xfer(unsigned int addr, char *reg,
+ int reg_len, char *buf, int num,
+ int tran_flag)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = addr;
+ msg[0].len = reg_len;
+ msg[0].buf = reg;
+ msg[0].flags = tran_flag;
+ msg[0].flags &= ~I2C_M_RD;
+
+ msg[1].addr = addr;
+ msg[1].len = num;
+ msg[1].buf = buf;
+ msg[1].flags = tran_flag;
+
+ if (tran_flag & MXC_I2C_FLAG_READ) {
+ msg[1].flags |= I2C_M_RD;
+ } else {
+ msg[1].flags &= ~I2C_M_RD;
+ }
+
+ ret = i2c_transfer(mc521da_i2c_client.adapter, msg, 2);
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+static int mc521da_read_reg(u8 * reg, u8 * val)
+{
+ return mc521da_i2c_client_xfer(MC521DA_I2C_ADDRESS, reg, 1, val, 1,
+ MXC_I2C_FLAG_READ);
+}
+
+static int mc521da_write_reg(u8 reg, u8 val)
+{
+ u8 temp1, temp2;
+ temp1 = reg;
+ temp2 = val;
+ return mc521da_i2c_client_xfer(MC521DA_I2C_ADDRESS, &temp1, 1, &temp2,
+ 1, 0);
+}
+
+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 reg[1], 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 */
+ reg[0] = 0x00;
+ while (i < 256) {
+ i++;
+ mc521da_read_reg(reg, &data);
+ 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 */
+ reg[0] = 0x00;
+ while (i < 256) {
+ i++;
+ mc521da_read_reg(reg, &data);
+ 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->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)
+{
+ u8 reg[1];
+ u8 *pdata;
+
+ *saturation = 0;
+
+ /* Select ISP */
+ mc521da_write_reg(0xff, 0x02);
+
+ reg[0] = 0x41;
+ pdata = (u8 *) bright;
+ mc521da_read_reg(reg, pdata);
+
+ reg[0] = 0xCA;
+ pdata = (u8 *) red;
+ mc521da_read_reg(reg, pdata);
+
+ reg[0] = 0xCB;
+ pdata = (u8 *) green;
+ mc521da_read_reg(reg, pdata);
+
+ reg[0] = 0xCC;
+ pdata = (u8 *) blue;
+ mc521da_read_reg(reg, pdata);
+}
+
+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..c34edf046b53
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.c
@@ -0,0 +1,731 @@
+/*
+ * 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 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 <asm/arch/mxc_i2c.h>
+#include "mxc_v4l2_capture.h"
+#include "mt9v111.h"
+
+#ifdef MT9V111_DEBUG
+static u16 testpattern = 0;
+#endif
+
+static sensor_interface *interface_param = NULL;
+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
+ */
+
+static u16 mt9v111_endian_swap16(u16 data)
+{
+ u16 temp;
+
+ temp = data;
+ temp = ((data >> 8) & 0xff) | ((data << 8) & 0xff00);
+
+ return temp;
+}
+
+static int mt9v111_i2c_client_xfer(unsigned int addr, char *reg, int reg_len,
+ char *buf, int num, int tran_flag)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = addr;
+ msg[0].len = reg_len;
+ msg[0].buf = reg;
+ msg[0].flags = tran_flag;
+ msg[0].flags &= ~I2C_M_RD;
+
+ msg[1].addr = addr;
+ msg[1].len = num;
+ msg[1].buf = buf;
+ msg[1].flags = tran_flag;
+
+ if (tran_flag & MXC_I2C_FLAG_READ) {
+ msg[1].flags |= I2C_M_RD;
+ } else {
+ msg[1].flags &= ~I2C_M_RD;
+ }
+
+ ret = i2c_transfer(mt9v111_i2c_client.adapter, msg, 2);
+ if (ret >= 0)
+ return 0;
+
+ return ret;
+}
+
+#ifdef MT9V111_DEBUG
+static int mt9v111_read_reg(u8 * reg, u16 * val)
+{
+ return mt9v111_i2c_client_xfer(MT9V111_I2C_ADDRESS, reg, 1,
+ (u8 *) val, 2, MXC_I2C_FLAG_READ);
+}
+#endif
+
+static int mt9v111_write_reg(u8 reg, u16 val)
+{
+ u8 temp1;
+ u16 temp2;
+ temp1 = reg;
+ temp2 = mt9v111_endian_swap16(val);
+ pr_debug("write reg %x val %x.\n", reg, val);
+ return mt9v111_i2c_client_xfer(MT9V111_I2C_ADDRESS, &temp1, 1,
+ (u8 *) & temp2, 2, 0);
+}
+
+/*!
+ * 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->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)
+{
+ u8 reg;
+ u16 data;
+
+ // switch to sensor registers
+ reg = MT9V111I_ADDR_SPACE_SEL;
+ data = MT9V111I_SEL_SCA;
+ mt9v111_write_reg(reg, data);
+
+ if (flag == true) {
+ testpattern = MT9V111S_OUTCTRL_TEST_MODE;
+
+ reg = MT9V111S_ROW_NOISE_CTRL;
+ data = mt9v111_read_reg(&reg, &data) & 0xBF;
+ data = mt9v111_endian_swap16(data);
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_TEST_DATA;
+ data = 0;
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_OUTPUT_CTRL;
+ // changes take effect
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(reg, data);
+ } else {
+ testpattern = 0;
+
+ reg = MT9V111S_ROW_NOISE_CTRL;
+ data = mt9v111_read_reg(&reg, &data) | 0x40;
+ data = mt9v111_endian_swap16(data);
+ mt9v111_write_reg(reg, data);
+
+ reg = MT9V111S_OUTPUT_CTRL;
+ // changes take effect
+ data = MT9V111S_OUTCTRL_CHIP_ENABLE | testpattern | 0x3000;
+ mt9v111_write_reg(reg, 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/mt9v111.h b/drivers/media/video/mxc/capture/mt9v111.h
new file mode 100644
index 000000000000..21a16f1e214d
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mt9v111.h
@@ -0,0 +1,421 @@
+/*
+ * 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 Camera Sensor Drivers
+ */
+
+/*!
+ * @file mt9v111.h
+ *
+ * @brief MT9V111 Camera Header file
+ *
+ * It include all the defines for bitmaps operations, also two main structure
+ * one for IFP interface structure, other for sensor core registers.
+ *
+ * @ingroup Camera
+ */
+
+#ifndef MT9V111_H_
+#define MT9V111_H_
+
+/*!
+ * 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..0ff86510d83c
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.c
@@ -0,0 +1,332 @@
+/*
+ * 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_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 <asm/arch/clock.h>
+#include <asm/arch/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(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(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..0d5fadc40122
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_csi.h
@@ -0,0 +1,165 @@
+/*
+ * 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_csi.h
+ *
+ * @brief CMOS Sensor interface functions
+ *
+ * @ingroup CSI
+ */
+
+#ifndef MX27_CSI_H
+#define MX27_CSI_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..1f93e32d36c1
--- /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(INT_EMMAPRP);
+ if (request_irq(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(INT_EMMAPRP);
+ free_irq(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/mx27_v4l2_capture.c b/drivers/media/video/mxc/capture/mx27_v4l2_capture.c
new file mode 100644
index 000000000000..e6045f609b50
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mx27_v4l2_capture.c
@@ -0,0 +1,2077 @@
+/*
+ * 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_v4l2_capture.c
+ *
+ * @brief MX27 Video For Linux 2 driver
+ *
+ * @ingroup MXC_V4L2_CAPTURE
+ */
+
+#include <linux/module.h>
+#include <linux/init.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/version.h>
+#include <media/v4l2-dev.h>
+#include <asm/io.h>
+#include <asm/semaphore.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 = dev->priv;
+ 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 = dev->priv;
+
+ /* 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 <asm/arch/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 = 0;
+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 = dev->priv;
+ 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 = dev->priv;
+
+ 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 = dev->priv;
+ 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 = dev->priv;
+
+ 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 = dev->priv;
+ 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 = {
+ .owner = THIS_MODULE,
+ .name = "Mxc Camera",
+ .type = 0,
+ .type2 = VID_TYPE_CAPTURE,
+ .hardware = 0,
+ .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;
+ 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;
+
+ if ((g_cam = cam = kmalloc(sizeof(cam_data), GFP_KERNEL)) == NULL) {
+ pr_debug("failed to mxc_v4l_register_camera\n");
+ return -1;
+ }
+
+ 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/mxc_v4l2_capture.c b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
new file mode 100644
index 000000000000..04c52e0a9bea
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.c
@@ -0,0 +1,1865 @@
+/*
+ * 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 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 <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/arch/mxcfb.h>
+#include "mxc_v4l2_capture.h"
+#include "ipu_prp_sw.h"
+
+static int csi_mclk_flag_backup;
+static int video_nr = -1;
+static cam_data *g_cam;
+
+#define MXC_V4L2_CAPTURE_NUM_OUTPUTS 2
+static struct v4l2_output mxc_capture_outputs[MXC_V4L2_CAPTURE_NUM_OUTPUTS] = {
+ {
+ .index = 0,
+ .name = "DISP3",
+ .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,
+ }
+};
+
+/*!
+ * 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) {
+ printk(KERN_ERR "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)
+{
+ if (buf->index < 0 || buf->index >= FRAME_NUM) {
+ printk(KERN_ERR
+ "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 (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->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;
+
+ if (cam->capture_on == false)
+ return 0;
+
+ if (cam->enc_disable) {
+ err = cam->enc_disable(cam);
+ }
+ mxc_free_frames(cam);
+ cam->capture_on = false;
+ return err;
+}
+
+/*!
+ * Valid 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));
+}
+
+/*!
+ * 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;
+ int *width, *height;
+
+ do {
+ cam->overlay_fb = (struct fb_info *)registered_fb[i];
+ if (cam->overlay_fb == NULL) {
+ printk(KERN_ERR "verify_preview No matched.\n");
+ return -1;
+ }
+ if (strncmp(cam->overlay_fb->fix.id,
+ mxc_capture_outputs[cam->output].name, 5) == 0) {
+ break;
+ }
+ } while (++i < FB_MAX);
+
+ /* 4 bytes alignment for both FG and BG */
+ 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 > cam->overlay_fb->var.xres) {
+ printk(KERN_ERR "width exceed resize limit.\n");
+ return -1;
+ }
+ printk(KERN_ERR "width exceed 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 > cam->overlay_fb->var.yres) {
+ printk(KERN_ERR "height exceed resize limit.\n");
+ return -1;
+ }
+ printk(KERN_ERR "height exceed 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;
+#if defined(CONFIG_MXC_IPU_PRP_VF_SDC) || defined(CONFIG_MXC_IPU_PRP_VF_SDC_MODULE)
+ if (cam->output == 0) {
+ 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)
+ if (cam->output == 1) {
+ err = prp_vf_adc_select(cam);
+ if (err != 0)
+ return err;
+
+ err = cam->vf_start_adc(cam);
+ }
+#endif
+
+ 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;
+
+#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) {
+ 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;
+}
+
+/*!
+ * 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 = cam->v2f.fmt.pix;
+ 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;
+ int *width, *height;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (!valid_mode(f->fmt.pix.pixelformat)) {
+ printk(KERN_ERR
+ "mxc_v4l2_s_fmt: format not supported\n");
+ 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;
+ printk(KERN_ERR "width exceed 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;
+ printk(KERN_ERR "height exceed 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;
+ 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;
+ }
+ }
+ 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:
+ if (cam->rotation == IPU_ROTATE_HORIZ_FLIP)
+ c->value = 1;
+ break;
+ case V4L2_CID_VFLIP:
+ if (cam->rotation == IPU_ROTATE_VERT_FLIP)
+ c->value = 1;
+ 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_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_set_v42l_control(cam_data * cam, struct v4l2_control *c)
+{
+ switch (c->id) {
+ case V4L2_CID_HFLIP:
+ 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:
+ 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:
+ switch (c->value) {
+ case V4L2_MXC_ROTATE_NONE:
+ cam->rotation = IPU_ROTATE_NONE;
+ break;
+ case V4L2_MXC_ROTATE_VERT_FLIP:
+ cam->rotation = IPU_ROTATE_VERT_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_HORIZ_FLIP:
+ cam->rotation = IPU_ROTATE_HORIZ_FLIP;
+ break;
+ case V4L2_MXC_ROTATE_180:
+ cam->rotation = IPU_ROTATE_180;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT:
+ cam->rotation = IPU_ROTATE_90_RIGHT;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_VFLIP:
+ cam->rotation = IPU_ROTATE_90_RIGHT_VFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_RIGHT_HFLIP:
+ cam->rotation = IPU_ROTATE_90_RIGHT_HFLIP;
+ break;
+ case V4L2_MXC_ROTATE_90_LEFT:
+ cam->rotation = IPU_ROTATE_90_LEFT;
+ 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;
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ cam->cam_sensor->set_color(cam->bright, cam->saturation,
+ cam->red, cam->green, cam->blue);
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_BLACK_LEVEL:
+ cam->ae_mode = c->value & 0x03;
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ if (cam->cam_sensor->set_ae_mode)
+ cam->cam_sensor->set_ae_mode(cam->ae_mode);
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, false, false);
+ break;
+ case V4L2_CID_MXC_FLASH:
+ ipu_csi_flash_strobe(true);
+ 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;
+ ipu_csi_signal_cfg_t csi_param;
+ int err = 0;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ printk(KERN_ERR "mxc_v4l2_s_param invalid type\n");
+ return -EINVAL;
+ }
+
+ if (parm->parm.capture.timeperframe.denominator >
+ cam->standard.frameperiod.denominator) {
+ printk(KERN_ERR "mxc_v4l2_s_param frame rate %d larger "
+ "than standard supported %d\n",
+ parm->parm.capture.timeperframe.denominator,
+ cam->standard.frameperiod.denominator);
+ return -EINVAL;
+ }
+
+ /* Stop the viewfinder */
+ if (cam->overlay_on == true) {
+ stop_preview(cam);
+ }
+
+ cam->streamparm.parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->config
+ (&parm->parm.capture.timeperframe.denominator,
+ parm->parm.capture.capturemode);
+ ipu_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)) {
+ printk(KERN_ERR
+ "mxc_v4l2_s_param frame un-supported capture mode\n");
+ err = -EINVAL;
+ goto exit;
+ }
+
+ if (parm->parm.capture.capturemode ==
+ cam->streamparm.parm.capture.capturemode) {
+ goto exit;
+ }
+
+ /* 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;
+ ipu_csi_init_interface(param->width, param->height,
+ param->pixel_fmt, csi_param);
+ ipu_csi_set_window_size(param->width + 1, param->height + 1);
+
+ 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;
+ }
+
+ exit:
+ if (cam->overlay_on == true) {
+ start_preview(cam);
+ }
+
+ return err;
+}
+
+/*!
+ * 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;
+ 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)
+{
+ sensor_interface *param;
+ ipu_csi_signal_cfg_t csi_param;
+ struct video_device *dev = video_devdata(file);
+ cam_data *cam = dev->priv;
+ int err = 0;
+
+ if (!cam) {
+ printk(KERN_ERR "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 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);
+
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, true, true);
+ param = cam->cam_sensor->reset();
+ if (param == NULL) {
+ cam->open_count--;
+ ipu_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;
+ ipu_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);
+
+ /* pr_info("mxc_v4l_open saturation %x ae_mode %x\n",
+ cam->saturation, cam->ae_mode); */
+
+ ipu_csi_enable_mclk(CSI_MCLK_I2C, 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 = dev->priv;
+
+ if (!cam) {
+ printk(KERN_ERR "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 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_PRP_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 = dev->priv;
+
+ 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)) {
+ printk(KERN_ERR "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 = dev->priv;
+ int retval = 0;
+ unsigned long lock_flags;
+
+ 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) {
+ printk(KERN_ERR
+ "VIDIOC_REQBUFS: not enough buffer\n");
+ req->count = FRAME_NUM;
+ }
+
+ if ((req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (req->memory != V4L2_MEMORY_MMAP)) {
+ printk(KERN_ERR
+ "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) {
+ printk(KERN_ERR
+ "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;
+
+ 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) {
+ printk(KERN_ERR
+ "VIDIOC_QBUF: buffer already queued\n");
+ } else if (cam->frame[index].buffer.
+ flags & V4L2_BUF_FLAG_DONE) {
+ printk(KERN_ERR
+ "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:{
+ retval = mxc_streamon(cam);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_STREAMOFF ioctl
+ */
+ case VIDIOC_STREAMOFF:{
+ retval = mxc_streamoff(cam);
+ 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;
+
+ 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;
+
+ ipu_csi_set_window_size(cam->crop_current.width,
+ cam->crop_current.height);
+ ipu_csi_set_window_pos(cam->crop_current.left,
+ cam->crop_current.top);
+ break;
+ }
+
+ /*!
+ * V4l2 VIDIOC_OVERLAY ioctl
+ */
+ case VIDIOC_OVERLAY:{
+ int *on = arg;
+ 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;
+ *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;
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ printk(KERN_ERR "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;
+ printk(KERN_ERR "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 >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ retval = -EINVAL;
+ break;
+ }
+
+ *output = mxc_capture_outputs[output->index];
+
+ 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 >= MXC_V4L2_CAPTURE_NUM_OUTPUTS) {
+ 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 = dev->priv;
+
+ 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)) {
+ printk(KERN_ERR "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 = dev->priv;
+ 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 = {
+ .owner = THIS_MODULE,
+ .name = "Mxc Camera",
+ .type = 0,
+ .type2 = VID_TYPE_CAPTURE,
+ .hardware = 0,
+ .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.
+*
+* @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;
+
+ 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->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 {
+ 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)
+{
+ /* 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);
+ ipu_csi_set_window_pos(cam->crop_current.left, cam->crop_current.top);
+ 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.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->cam_sensor = &camera_sensor_if;
+ cam->enc_callback = camera_callback;
+ init_waitqueue_head(&cam->power_queue);
+ cam->int_lock = SPIN_LOCK_UNLOCKED;
+ 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();
+ ipu_csi_enable_mclk(csi_mclk_flag_backup, true, true);
+ } else {
+ csi_mclk_flag_backup = ipu_csi_read_mclk_flag();
+ ipu_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",
+ },
+ .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;
+
+ /* Register the device driver structure. */
+ err = platform_driver_register(&mxc_v4l2_driver);
+ if (err != 0) {
+ printk("camera_init: platform_driver_register failed.\n");
+ return err;
+ }
+
+ if ((g_cam = kmalloc(sizeof(cam_data), GFP_KERNEL)) == NULL) {
+ printk(KERN_ERR "failed to mxc_v4l_register_camera\n");
+ return -1;
+ }
+
+ init_camera_struct(g_cam);
+
+ /* Register the I2C device */
+ err = platform_device_register(&mxc_v4l2_devices);
+ if (err != 0) {
+ printk(KERN_ERR
+ "camera_init: platform_device_register failed.\n");
+ video_device_release(g_cam->video_dev);
+ kfree(g_cam);
+ g_cam = NULL;
+ }
+
+ /* register v4l 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);
+ video_device_release(g_cam->video_dev);
+ kfree(g_cam);
+ g_cam = NULL;
+ printk(KERN_ERR "video_register_device failed\n");
+ return -1;
+ }
+
+ return err;
+}
+
+/*!
+ * Exit and cleanup for the V4L2
+ *
+ */
+static void __exit camera_exit(void)
+{
+ pr_info("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) {
+ printk(KERN_ERR "camera open -- setting ops to NULL\n");
+ } else {
+ pr_info("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/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
new file mode 100644
index 000000000000..b76908a62c71
--- /dev/null
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
@@ -0,0 +1,177 @@
+/*
+ * 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 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 <media/v4l2-dev.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/mxc_v4l2.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;
+};
+
+typedef struct {
+ u8 clk_mode;
+ u8 ext_vsync;
+ u8 Vsync_pol;
+ u8 Hsync_pol;
+ u8 pixclk_pol;
+ u8 data_pol;
+ u8 data_width;
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u32 mclk;
+} sensor_interface;
+
+/* Sensor control function */
+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);
+};
+
+/*!
+ * common v4l2 driver structure.
+ */
+typedef struct _cam_data {
+ struct video_device *video_dev;
+
+ /* semaphore guard against SMP multithreading */
+ struct semaphore busy_lock;
+
+ int open_count;
+
+ /* params lock for this camera */
+ struct semaphore param_lock;
+
+ /* Encorder */
+ 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;
+ 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;
+
+ /* standart */
+ struct v4l2_streamparm streamparm;
+ struct v4l2_standard standard;
+
+ /* 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;
+
+ /* camera sensor interface */
+ struct camera_sensor *cam_sensor;
+} cam_data;
+
+void set_mclk_rate(uint32_t * p_mclk_freq);
+#endif /* __MXC_V4L2_CAPTURE_H__ */
diff --git a/drivers/media/video/mxc/capture/s5k3aaex.c b/drivers/media/video/mxc/capture/s5k3aaex.c
new file mode 100644
index 000000000000..455d55e4aa55
--- /dev/null
+++ b/drivers/media/video/mxc/capture/s5k3aaex.c
@@ -0,0 +1,572 @@
+/*
+ * 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 s5k3aaex.c
+ *
+ * @brief s5k3aaex 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 "asm/arch/mxc_i2c.h"
+#include "s5k3aaex.h"
+#include "mxc_v4l2_capture.h"
+
+static s5k3aaex_conf s5k3aaex_device;
+static sensor_interface *interface_param = NULL;
+
+static int s5k3aaex_attach(struct i2c_adapter *adapter);
+static int s5k3aaex_detach(struct i2c_client *client);
+
+static struct i2c_driver s5k3aaex_i2c_driver = {
+ .owner = THIS_MODULE,
+ .name = "S5K3AAEX Client",
+ .flags = I2C_DF_NOTIFY,
+ .attach_adapter = s5k3aaex_attach,
+ .detach_client = s5k3aaex_detach,
+};
+
+static struct i2c_client s5k3aaex_i2c_client = {
+ .name = "s5k3aaex I2C dev",
+ .id = 1,
+ .addr = S5K3AAEX_I2C_ADDRESS,
+ .driver = &s5k3aaex_i2c_driver,
+};
+
+static s5k3aaex_image_format format[2] = {
+ {
+ .index = 0,
+ .imageFormat = S5K3AAEX_OutputResolution_SXGA,
+ .width = S5K3AAEX_WINWIDTH,
+ .height = S5K3AAEX_WINHEIGHT,
+ },
+ {
+ .index = 1,
+ .imageFormat = S5K3AAEX_OutputResolution_VGA,
+ .width = 640,
+ .height = 512,
+ },
+};
+
+extern void gpio_sensor_setup(void);
+extern void gpio_sensor_reset(bool flag);
+extern void gpio_sensor_suspend(bool flag);
+
+/*
+ * Function definitions
+ */
+static int s5k3aaex_i2c_client_xfer(unsigned int addr, char *reg,
+ int reg_len, char *buf, int num,
+ int tran_flag)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ msg[0].addr = addr;
+ msg[0].len = reg_len;
+ msg[0].buf = reg;
+ msg[0].flags = tran_flag;
+ msg[0].flags &= ~I2C_M_RD;
+
+ msg[1].addr = addr;
+ msg[1].len = num;
+ msg[1].buf = buf;
+ msg[1].flags = tran_flag;
+
+ if (tran_flag & MXC_I2C_FLAG_READ) {
+ msg[1].flags |= I2C_M_RD;
+ } else {
+ msg[1].flags &= ~I2C_M_RD;
+ }
+
+ ret = i2c_transfer(s5k3aaex_i2c_client.adapter, msg, 2);
+ if (ret >= 0)
+ return 0;
+ return ret;
+}
+
+static int s5k3aaex_read_reg(u8 * reg, u8 * val)
+{
+ msleep(100);
+ return s5k3aaex_i2c_client_xfer(S5K3AAEX_I2C_ADDRESS, reg, 1, val, 1,
+ MXC_I2C_FLAG_READ);
+}
+
+static int s5k3aaex_write_reg(u8 reg, u8 val)
+{
+ u8 temp1, temp2;
+ temp1 = reg;
+ temp2 = val;
+ msleep(100);
+ return s5k3aaex_i2c_client_xfer(S5K3AAEX_I2C_ADDRESS, &temp1, 1, &temp2,
+ 1, 0);
+}
+
+static u8 s5k3aaex_sensor_downscale(bool downscale)
+{
+ u8 error = 0;
+ u8 reg;
+ u8 data;
+
+ if (downscale == true) {
+ pr_info("VGA\n");
+ reg = 0xEC;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+
+ reg = 0x2;
+ data = 0x30;
+ s5k3aaex_write_reg(reg, data);
+ } else {
+ pr_info("SXGA\n");
+ reg = 0xEC;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+
+ reg = 0x2;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+ }
+
+ return error;
+}
+
+/*!
+ * Initialize s5k3aaex_sensor_lib
+ * Libarary for Sensor configuration through I2C
+ *
+ * @param page0 page0 Registers
+ * @param page2 page2 Registers
+ *
+ * @return status
+ */
+static u8 s5k3aaex_sensor_lib(s5k3aaex_page0 * page0, s5k3aaex_page2 * page2)
+{
+ u8 error = 0;
+ u8 reg;
+ u8 data;
+
+ // changed to ARM command register map page 0
+ reg = 0xEC;
+ data = page0->addressSelect;
+ s5k3aaex_write_reg(reg, data);
+ // set the main clock
+ reg = 0x72;
+ data = page0->mainClock;
+ s5k3aaex_write_reg(reg, data);
+
+ // changed to CIS register map page 02
+ reg = 0xEC;
+ data = page2->addressSelect;
+ s5k3aaex_write_reg(reg, data);
+
+ // write the Hblank width
+ reg = 0x1e;
+ data = (u8) (page2->hblank & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+ reg = 0x1d;
+ data = (u8) ((page2->hblank >> 8) & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+
+ // write the Vblank width
+ reg = 0x18;
+ data = (u8) (page2->vblank & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+ reg = 0x17;
+ data = (u8) ((page2->vblank >> 8) & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+
+ // write the WRP
+ reg = 0x5;
+ data = (u8) (page2->wrp & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+ reg = 0x4;
+ data = (u8) ((page2->wrp >> 8) & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+
+ // write the WCP
+ reg = 0x7;
+ data = (u8) (page2->wcp & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+ reg = 0x6;
+ data = (u8) ((page2->wcp >> 8) & 0xFF);
+ s5k3aaex_write_reg(reg, data);
+
+ // write DEFCOR_MOV_ADC 8 bit
+ reg = 0x2;
+ data = 0x8;
+ s5k3aaex_write_reg(reg, data);
+
+ // changed to ARM command register map page 1
+ reg = 0xEC;
+ data = 1;
+ s5k3aaex_write_reg(reg, data);
+
+ // write size to itu r 601
+ reg = 0x6a;
+ data = 0x5;
+ s5k3aaex_write_reg(reg, data);
+
+ return error;
+}
+
+/*!
+ * s5k3aaex sensor interface Initialization
+ * @param param sensor_interface *
+ * @param width u32
+ * @param height u32
+ * @return None
+ */
+static void s5k3aaex_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 = 0x1;
+ param->width = width - 1;
+ param->height = ((height == 512) ? 480 : height) - 1;
+ param->pixel_fmt = IPU_PIX_FMT_UYVY;
+}
+
+static int s5k3aaex_rate_cal(int *frame_rate, int mclk)
+{
+ int num_clock_per_row;
+ int max_rate = 0;
+ int index = 0;
+ u16 width;
+ u16 height;
+
+ do {
+ s5k3aaex_device.page0->imageFormat = format[index].imageFormat;
+ height = format[index].height;
+ width = format[index++].width;
+ s5k3aaex_device.page2->hblank = S5K3AAEX_HORZBLANK_DEFAULT;
+ s5k3aaex_device.page2->vblank = S5K3AAEX_VERTBLANK_DEFAULT;
+
+ num_clock_per_row = (width + s5k3aaex_device.page2->hblank) * 2;
+ max_rate = mclk / (num_clock_per_row *
+ (height + s5k3aaex_device.page2->vblank));
+ } while ((index < 2) && (max_rate < *frame_rate));
+
+ s5k3aaex_interface(interface_param, width, height);
+
+ if (max_rate < *frame_rate)
+ *frame_rate = max_rate;
+ if (*frame_rate == 0)
+ *frame_rate = max_rate;
+
+ s5k3aaex_device.page2->vblank = mclk /
+ (*frame_rate * num_clock_per_row) - height;
+
+ return index;
+}
+
+/*!
+ * s5k3aaex sensor configuration
+ *
+ * @param frame_rate int *
+ * @param high_quality int
+ * @return sensor_interface *
+ */
+static sensor_interface *s5k3aaex_config(int *frame_rate, int high_quality)
+{
+ int index;
+
+ index = s5k3aaex_rate_cal(frame_rate, interface_param->mclk);
+
+ if (index == 1) {
+ s5k3aaex_sensor_downscale(false);
+ } else {
+ s5k3aaex_sensor_downscale(true);
+ }
+
+ s5k3aaex_device.page0->mainClock = interface_param->mclk / 1048576 * 5;
+ s5k3aaex_sensor_lib(s5k3aaex_device.page0, s5k3aaex_device.page2);
+
+ return interface_param;
+}
+
+/*!
+ * s5k3aaex sensor set color configuration
+ *
+ * @param bright int
+ * @param saturation int
+ * @param red int
+ * @param green int
+ * @param blue int
+ * @return None
+ */
+static void s5k3aaex_set_color(int bright, int saturation, int red, int green,
+ int blue)
+{
+ u8 reg;
+ u8 data;
+
+ reg = 0xEC;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+
+ // set Brightness/Color Level balance
+ reg = 0x76;
+ data = (u8) bright;
+ s5k3aaex_write_reg(reg, data);
+ reg = 0x77;
+ data = (u8) saturation;
+ s5k3aaex_write_reg(reg, data);
+
+ reg = 0xEC;
+ data = 1;
+ s5k3aaex_write_reg(reg, data);
+
+ // set Red
+ reg = 0x10;
+ data = (u8) red;
+ s5k3aaex_write_reg(reg, data);
+ // set Blue
+ reg = 0x18;
+ data = (u8) blue;
+ s5k3aaex_write_reg(reg, data);
+}
+
+/*!
+ * s5k3aaex sensor get color configuration
+ *
+ * @param bright int *
+ * @param saturation int *
+ * @param red int *
+ * @param green int *
+ * @param blue int *
+ * @return None
+ */
+static void s5k3aaex_get_color(int *bright, int *saturation, int *red,
+ int *green, int *blue)
+{
+ u8 reg;
+ u8 data;
+ u8 *pdata;
+
+ reg = 0xEC;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+
+ // get Brightness/Color Level balance
+ reg = 0x76;
+ pdata = (u8 *) bright;
+ s5k3aaex_read_reg(&reg, pdata);
+
+ reg = 0x77;
+ pdata = (u8 *) saturation;
+ s5k3aaex_read_reg(&reg, pdata);
+
+ reg = 0xEC;
+ data = 1;
+ s5k3aaex_write_reg(reg, data);
+
+ // get Red
+ reg = 0x10;
+ pdata = (u8 *) red;
+ s5k3aaex_read_reg(&reg, pdata);
+
+ // get Blue
+ reg = 0x18;
+ pdata = (u8 *) blue;
+ s5k3aaex_read_reg(&reg, pdata);
+}
+
+/*!
+ * s5k3aaex Reset function
+ *
+ * @return None
+ */
+static sensor_interface *s5k3aaex_reset(void)
+{
+ set_mclk_rate(&interface_param->mclk);
+
+ /* Reset for 10 cycle */
+ gpio_sensor_reset(true);
+ msleep(10);
+ gpio_sensor_reset(false);
+ msleep(30);
+
+ s5k3aaex_interface(interface_param, format[0].width, format[0].height);
+ return interface_param;
+}
+
+struct camera_sensor camera_sensor_if = {
+ set_color:s5k3aaex_set_color,
+ get_color:s5k3aaex_get_color,
+ config:s5k3aaex_config,
+ reset:s5k3aaex_reset,
+};
+
+#if 0
+static void s5k3aaex_test_pattern(bool flag)
+{
+ u8 reg;
+ u8 data;
+
+ // changed to ARM command register map page 0
+ reg = 0xEC;
+ data = 0;
+ s5k3aaex_write_reg(reg, data);
+
+ if (flag == true) {
+ reg = 0xb;
+ data = 0x1;
+ s5k3aaex_write_reg(reg, data);
+ } else {
+ reg = 0xb;
+ data = 0x0;
+ s5k3aaex_write_reg(reg, data);
+ }
+}
+#endif
+
+/*!
+ * s5k3aaex I2C attach function
+ *
+ * @param adapter struct i2c_adapter *
+ * @return Error code indicating success or failure
+ */
+static int s5k3aaex_attach(struct i2c_adapter *adapter)
+{
+ if (strcmp(adapter->name, MXC_ADAPTER_NAME) != 0) {
+ printk(KERN_ERR "s5k3aaex_attach: %s\n", adapter->name);
+ return -1;
+ }
+
+ s5k3aaex_i2c_client.adapter = adapter;
+ if (i2c_attach_client(&s5k3aaex_i2c_client)) {
+ s5k3aaex_i2c_client.adapter = NULL;
+ printk(KERN_ERR "s5k3aaex_attach: i2c_attach_client failed\n");
+ return -1;
+ }
+
+ interface_param = (sensor_interface *)
+ kmalloc(sizeof(sensor_interface), GFP_KERNEL);
+ if (!interface_param) {
+ printk(KERN_ERR "s5k3aaex_attach: kmalloc failed \n");
+ return -1;
+ }
+
+ gpio_sensor_setup();
+
+ gpio_sensor_suspend(false);
+
+ interface_param->mclk = 0x2000000;
+
+ return 0;
+}
+
+/*!
+ * s5k3aaex I2C detach function
+ *
+ * @param client struct i2c_client *
+ * @return Error code indicating success or failure
+ */
+static int s5k3aaex_detach(struct i2c_client *client)
+{
+ int err;
+
+ if (!s5k3aaex_i2c_client.adapter)
+ return -1;
+
+ err = i2c_detach_client(&s5k3aaex_i2c_client);
+ s5k3aaex_i2c_client.adapter = NULL;
+
+ if (interface_param)
+ kfree(interface_param);
+ interface_param = NULL;
+
+ return err;
+}
+
+/*!
+ * s5k3aaex init function
+ *
+ * @return Error code indicating success or failure
+ */
+static __init int s5k3aaex_init(void)
+{
+ u8 err = 0;
+
+ s5k3aaex_device.page0 = (s5k3aaex_page0 *)
+ kmalloc(sizeof(s5k3aaex_page0), GFP_KERNEL);
+
+ if (!s5k3aaex_device.page0)
+ return -1;
+ memset(s5k3aaex_device.page0, 0, sizeof(s5k3aaex_page0));
+ s5k3aaex_device.page0->addressSelect = 0;
+ s5k3aaex_device.page0->functionOnOff = 0x48;
+
+ s5k3aaex_device.page2 = (s5k3aaex_page2 *)
+ kmalloc(sizeof(s5k3aaex_page2), GFP_KERNEL);
+ if (!s5k3aaex_device.page2) {
+ kfree(s5k3aaex_device.page0);
+ s5k3aaex_device.page0 = NULL;
+ return -1;
+ }
+ memset(s5k3aaex_device.page2, 0, sizeof(s5k3aaex_page2));
+ s5k3aaex_device.page2->addressSelect = 2;
+ s5k3aaex_device.page2->wrp = 14;
+ s5k3aaex_device.page2->wcp = 14;
+
+ s5k3aaex_device.page2->hblank = S5K3AAEX_HORZBLANK_DEFAULT;
+ s5k3aaex_device.page2->vblank = S5K3AAEX_VERTBLANK_DEFAULT;
+
+ err = i2c_add_driver(&s5k3aaex_i2c_driver);
+
+ return err;
+}
+
+/*!
+ * s5k3aaex cleanup function
+ *
+ * @return Error code indicating success or failure
+ */
+static void __exit s5k3aaex_clean(void)
+{
+ i2c_del_driver(&s5k3aaex_i2c_driver);
+
+ if (s5k3aaex_device.page0) {
+ kfree(s5k3aaex_device.page0);
+ s5k3aaex_device.page0 = NULL;
+ }
+
+ if (s5k3aaex_device.page2) {
+ kfree(s5k3aaex_device.page2);
+ s5k3aaex_device.page2 = NULL;
+ }
+}
+
+module_init(s5k3aaex_init);
+module_exit(s5k3aaex_clean);
+
+/* Exported symbols for modules. */
+EXPORT_SYMBOL(camera_sensor_if);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("s5k3aaex Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/mxc/capture/s5k3aaex.h b/drivers/media/video/mxc/capture/s5k3aaex.h
new file mode 100644
index 000000000000..61bcb15110b5
--- /dev/null
+++ b/drivers/media/video/mxc/capture/s5k3aaex.h
@@ -0,0 +1,88 @@
+/*
+ * 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 s5k3aaex.h
+ *
+ * @brief S5K3AAEX Camera Header file
+ *
+ * It include all the defines for bitmaps operations, also two main structure
+ * one for IFP interface structure, other for sensor core registers.
+ *
+ * @ingroup Camera
+ */
+
+#ifndef __S5K3AAEX_H__
+#define __S5K3AAEX_H__
+
+/*! I2C Slave Address */
+#define S5K3AAEX_I2C_ADDRESS 0x2d
+
+enum {
+ S5K3AAEX_OutputResolution_SXGA = 0,
+ S5K3AAEX_OutputResolution_VGA = 0x1c,
+ S5K3AAEX_OutputResolution_QVGA = 0x14,
+ S5K3AAEX_OutputResolution_QQVGA = 0x16,
+ S5K3AAEX_OutputResolution_CIF = 0x10,
+ S5K3AAEX_OutputResolution_QCIF = 0x12,
+};
+
+enum {
+ S5K3AAEX_WINWIDTH = 0x500,
+ S5K3AAEX_WINHEIGHT = 0x400,
+ S5K3AAEX_ROWSTART = 14,
+ S5K3AAEX_COLSTART = 14,
+
+ S5K3AAEX_HORZBLANK_DEFAULT = 142,
+ S5K3AAEX_VERTBLANK_DEFAULT = 101,
+};
+
+typedef struct {
+ u8 index;
+ u8 imageFormat;
+ u16 width;
+ u16 height;
+} s5k3aaex_image_format;
+
+/*!
+ * s5k3aaex ARM command Register page 0 structure.
+ */
+typedef struct {
+ u8 addressSelect;
+ u8 imageFormat;
+ u8 functionOnOff;
+ u8 mainClock;
+} s5k3aaex_page0;
+
+/*!
+ * s5k3aaex IFP Register structure.
+ */
+typedef struct {
+ u8 addressSelect;
+ u16 wrp;
+ u16 wcp;
+ u16 wrd;
+ u16 wcw;
+ u16 vblank;
+ u16 hblank;
+} s5k3aaex_page2;
+
+/*!
+ * s5k3aaex Config structure
+ */
+typedef struct {
+ s5k3aaex_page0 *page0;
+ s5k3aaex_page2 *page2;
+} s5k3aaex_conf;
+
+#endif /* __S5K3AAEX_H__ */
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..e2be4f44ad4f
--- /dev/null
+++ b/drivers/media/video/mxc/capture/sensor_clock.c
@@ -0,0 +1,56 @@
+/*
+ * 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 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>
+
+/*
+ * set_mclk_rate
+ *
+ * @param p_mclk_freq mclk frequence
+ *
+ */
+void set_mclk_rate(uint32_t * p_mclk_freq)
+{
+ struct clk *clk;
+ int i;
+ uint32_t freq = 0;
+ uint32_t step = *p_mclk_freq / 8;
+
+ clk = clk_get(NULL, "csi_clk");
+
+ for (i = 0; i <= 8; i++) {
+ freq = clk_round_rate(clk, *p_mclk_freq - (i * step));
+ if (freq <= *p_mclk_freq)
+ break;
+ }
+ clk_set_rate(clk, freq);
+
+ *p_mclk_freq = freq;
+
+ clk_put(clk);
+ pr_debug("mclk frequency = %d\n", *p_mclk_freq);
+}
+
+/* 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..b73cc58f3aa8
--- /dev/null
+++ b/drivers/media/video/mxc/output/Kconfig
@@ -0,0 +1,21 @@
+config VIDEO_MXC_IPU_OUTPUT
+ bool
+ depends on VIDEO_MXC_OUTPUT && MXC_IPU
+ default y
+ ---help---
+ This is the video4linux2 driver for IPU 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..1b4445a7a68d
--- /dev/null
+++ b/drivers/media/video/mxc/output/Makefile
@@ -0,0 +1,8 @@
+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
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..7d2785a71faf
--- /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(INT_EMMAPP);
+ return request_irq(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(INT_EMMAPP);
+ free_irq(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..144454a4faca
--- /dev/null
+++ b/drivers/media/video/mxc/output/mx27_v4l2_output.c
@@ -0,0 +1,1446 @@
+/*
+ * 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_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 <media/v4l2-dev.h>
+#include <asm/poll.h>
+#include <asm/io.h>
+#include <asm/semaphore.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 = {
+ .owner = THIS_MODULE,
+ .name = "MXC Video Output",
+ .type = 0,
+ .type2 = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING,
+ .hardware = 39,
+ .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) {
+ 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/mxc_v4l2_output.c b/drivers/media/video/mxc/output/mxc_v4l2_output.c
new file mode 100644
index 000000000000..08b0be63b2d7
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.c
@@ -0,0 +1,1710 @@
+/*
+ * 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 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 <asm/arch/mxcfb.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 spinlock_t g_lock = SPIN_LOCK_UNLOCKED;
+
+/* 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;
+
+ 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 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[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 */
+ 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[vout->next_rdy_ipu_buf] = index;
+ if (ipu_update_channel_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER,
+ vout->next_rdy_ipu_buf,
+ vout->v4l2_bufs[index].m.offset) < 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,
+ vout->next_rdy_ipu_buf) < 0) {
+ dev_err(vout->video_dev->dev,
+ "unable to set IPU buffer ready\n");
+ }
+ vout->next_rdy_ipu_buf = !vout->next_rdy_ipu_buf;
+
+ /* 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);
+}
+
+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;
+
+ spin_lock_irqsave(&g_lock, lock_flags);
+
+ g_irq_cnt++;
+
+ /* Process previous buffer */
+ 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);
+ /* printk("pp_irq: buf %d done\n", vout->next_done_ipu_buf); */
+ vout->next_done_ipu_buf = !vout->next_done_ipu_buf;
+ }
+
+ 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;
+}
+
+/*!
+ * 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;
+
+ 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] = dequeue_buf(&vout->ready_q);
+ vout->frame_count = 2;
+
+ ipu_enable_irq(IPU_IRQ_PP_IN_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);
+ if (vout->rotate < IPU_ROTATE_90_RIGHT) {
+ 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;
+ fbvar.xres = fbvar.xres_virtual = out_width;
+ fbvar.yres = out_height;
+ fbvar.yres_virtual = out_height * 2;
+ fbvar.bits_per_pixel = 16;
+ fb_set_var(fbi, &fbvar);
+
+ fb_pos.x = vout->crop_current.left;
+ fb_pos.y = vout->crop_current.top;
+ if (fbi->fbops->fb_ioctl)
+ fbi->fbops->fb_ioctl(fbi, MXCFB_SET_OVERLAY_POS,
+ (unsigned long)&fb_pos);
+
+ vout->display_bufs[0] = fbi->fix.smem_start;
+ vout->display_bufs[1] = fbi->fix.smem_start +
+ (fbi->fix.line_length * fbi->var.yres);
+ vout->display_buf_size = vout->crop_current.width *
+ vout->crop_current.height *
+ fmt_to_bpp(SDC_FG_FB_FORMAT) / 8;
+
+ if (vout->cur_disp_output == 3)
+ vout->display_ch = MEM_SDC_FG;
+ else
+ vout->display_ch = MEM_SDC_BG;
+
+ 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;
+ 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_pixel_fmt = SDC_FG_FB_FORMAT;
+ 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[1]].m.
+ offset, vout->offset.u_offset,
+ vout->offset.v_offset) != 0) {
+ dev_err(dev, "Error initializing PP input buffer\n");
+ return -EINVAL;
+ }
+
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ 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 */
+ 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,
+ out_height, out_width,
+ vout->rotate,
+ 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(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);
+ ipu_select_buffer(vout->post_proc_ch, IPU_INPUT_BUFFER, 1);
+
+ if (use_direct_adc == false) {
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 0);
+ ipu_select_buffer(vout->post_proc_ch, IPU_OUTPUT_BUFFER, 1);
+
+ ipu_enable_channel(vout->post_proc_ch);
+ if ((vout->display_ch == MEM_SDC_FG) ||
+ (vout->display_ch == MEM_SDC_BG)) {
+ 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);
+
+ spin_unlock_irqrestore(&g_lock, lockflag);
+
+ if (vout->post_proc_ch == MEM_PP_MEM) { /* SDC or ADC with Rotation */
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT) {
+ 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);
+ } else {
+ ipu_unlink_channels(MEM_PP_MEM, vout->display_ch);
+ }
+ ipu_disable_channel(MEM_PP_MEM, true);
+ if ((vout->display_ch != MEM_SDC_FG) &&
+ (vout->display_ch != MEM_SDC_BG)) {
+ 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);
+ }
+
+ ipu_uninit_channel(MEM_PP_MEM);
+ if (vout->rotate >= IPU_ROTATE_90_RIGHT)
+ 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;
+
+ if (vout->display_bufs[0] != 0) {
+ mxc_free_buffers(vout->display_bufs,
+ vout->display_bufs_vaddr, 2,
+ vout->display_buf_size);
+ }
+#ifdef CONFIG_FB_MXC_ASYNC_PANEL
+ if (vout->cur_disp_output < DISP3) {
+ 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_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:
+ 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;
+ }
+ }
+
+ 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);
+
+ 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 = 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);
+
+ 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, 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 */
+ 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;
+
+ if ((*p_output_num >= 5) ||
+ (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;
+ vout->crop_current =
+ vout->crop_bounds[vout->cur_disp_output];
+ 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,
+ .hardware = 0,
+ .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) &&
+ (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..6fb6f5a667bb
--- /dev/null
+++ b/drivers/media/video/mxc/output/mxc_v4l2_output.h
@@ -0,0 +1,130 @@
+/*
+ * 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 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 <asm/arch/ipu.h>
+#include <asm/arch/mxc_v4l2.h>
+
+#define MIN_FRAME_NUM 2
+#define MAX_FRAME_NUM 30
+
+#define MXC_V4L2_OUT_NUM_OUTPUTS 5
+#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];
+ 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;
+ 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;
+} vout_data;
+
+#endif
+#endif /* __MXC_V4L2_OUTPUT_H__ */
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5fef6783c716..625dbdcf7142 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -130,3 +130,12 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N.
+ If unsure, 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.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3877c87e6da2..9d00dc1e1600 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
+obj-$(CONFIG_MMC_MXC) += mxc_mmc.o
diff --git a/drivers/mmc/host/mxc_mmc.c b/drivers/mmc/host/mxc_mmc.c
new file mode 100644
index 000000000000..29d23824c1a8
--- /dev/null
+++ b/drivers/mmc/host/mxc_mmc.c
@@ -0,0 +1,1394 @@
+/*
+ * 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-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_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/delay.h>
+#include <linux/timer.h>
+#include <linux/clk.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 <asm/arch/mmc.h>
+
+#include "mxc_mmc.h"
+
+#if defined(CONFIG_MXC_MC13783_POWER)
+#include <asm/arch/pmic_power.h>
+#endif
+
+#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+
+static const int vdd_mapping[] = {
+ 0, 0,
+ 0, /* MMC_VDD_160 */
+ 0, 0,
+ 1, /* MMC_VDD_180 */
+ 0,
+ 2, /* MMC_VDD_200 */
+ 0, 0, 0, 0, 0,
+ 3, /* MMC_VDD_260 */
+ 4, /* MMC_VDD_270 */
+ 5, /* MMC_VDD_280 */
+ 6, /* MMC_VDD_290 */
+ 7, /* MMC_VDD_300 */
+ 7, /* MMC_VDD_310 - HACK for LP1070, actually 3.0V */
+ 7, /* MMC_VDD_320 - HACK for LP1070, actually 3.0V */
+ 0, 0, 0, 0
+};
+
+/*
+ * 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;
+
+ /*!
+ * 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;
+
+ /*!
+ * 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
+
+/*!
+ * 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_IPG_CLK_GATE_DIS |
+ STR_STP_CLK_IPG_PERCLK_GATE_DIS |
+ 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_IPG_CLK_GATE_DIS |
+ STR_STP_CLK_IPG_PERCLK_GATE_DIS |
+ 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 (!(cmdat & CMD_DAT_CONT_DATA_ENABLE) || (cmdat & CMD_DAT_CONT_WRITE)) {
+ mxcmci_start_clock(host, true);
+ } else {
+ __raw_writel(STR_STP_CLK_IPG_CLK_GATE_DIS |
+ STR_STP_CLK_IPG_PERCLK_GATE_DIS,
+ host->base + MMC_STR_STP_CLK);
+ }
+}
+
+/*!
+ * 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;
+
+ if (!(req->cmd->flags & MMC_KEEP_CLK_RUN)) {
+ mxcmci_stop_clock(host, true);
+ }
+ 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 TIMEOUT\n", DRIVER_NAME);
+ cmd->error = MMC_ERR_TIMEOUT;
+ } 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 crc error\n", DRIVER_NAME);
+ cmd->error = MMC_ERR_BADCRC;
+ }
+
+ /* 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 != MMC_ERR_NONE) {
+ /* 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 *)(page_address(data->sg->page) + data->sg->offset);
+ 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))) ;
+
+ /* read 32 bit data */
+ temp_data = __raw_readl(host->base + MMC_BUFFER_ACCESS);
+ 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) {
+ pr_debug("%s: Read time out occurred\n", DRIVER_NAME);
+ data->error = MMC_ERR_TIMEOUT;
+ __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 = MMC_ERR_BADCRC;
+ __raw_writel(STATUS_READ_CRC_ERR,
+ host->base + MMC_STATUS);
+ }
+ __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) {
+ pr_debug("%s: Write CRC error occurred\n", DRIVER_NAME);
+ data->error = MMC_ERR_BADCRC;
+ __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) {
+ pr_debug("%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 == MMC_ERR_NONE) {
+ 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) {
+ 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, IRQT_FALLING);
+ } else {
+ set_irq_type(host->detect_irq, IRQT_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;
+ unsigned int status = 0;
+ u32 intctrl;
+
+ if (host->mxc_mmc_suspend_flag == 1) {
+ clk_enable(host->clk);
+ }
+
+ status = __raw_readl(host->base + MMC_STATUS);
+ intctrl = __raw_readl(host->base + MMC_INT_CNTR);
+#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);
+ }
+
+ 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);
+#ifdef MXC_MMC_DMA_ENABLE
+ mxc_dma_device_t dev_id = 0;
+#endif
+
+#if defined(CONFIG_MXC_MC13783_POWER)
+ t_regulator_voltage voltage;
+#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) {
+ pr_debug("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 defined(CONFIG_MXC_MC13783_POWER)
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ if (host->id == 0) {
+ voltage.vmmc1 = vdd_mapping[ios->vdd];
+ pmic_power_regulator_set_voltage(REGU_VMMC1, voltage);
+ pmic_power_regulator_set_lp_mode(REGU_VMMC1,
+ LOW_POWER_DISABLED);
+ pmic_power_regulator_on(REGU_VMMC1);
+ }
+ if (host->id == 1) {
+ voltage.vmmc2 = vdd_mapping[ios->vdd];
+ pmic_power_regulator_set_voltage(REGU_VMMC2, voltage);
+ pmic_power_regulator_set_lp_mode(REGU_VMMC2,
+ LOW_POWER_DISABLED);
+ pmic_power_regulator_on(REGU_VMMC2);
+ }
+ pr_debug("mmc power on\n");
+ msleep(300);
+ break;
+ case MMC_POWER_OFF:
+ if (host->id == 0) {
+ pmic_power_regulator_set_lp_mode(REGU_VMMC1,
+ LOW_POWER_EN);
+ pmic_power_regulator_off(REGU_VMMC1);
+ }
+
+ if (host->id == 1) {
+ pmic_power_regulator_set_lp_mode(REGU_VMMC2,
+ LOW_POWER_EN);
+ pmic_power_regulator_off(REGU_VMMC2);
+ }
+ pr_debug("mmc power off\n");
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /*
+ * 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);
+ }
+}
+
+/*!
+ * 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
+};
+
+#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;
+ struct mmc_data *data = host->data;
+ u32 status;
+ ulong nob, blk_size, i, blk_len;
+
+ mxc_dma_disable(host->dma);
+
+ if (error) {
+ pr_debug("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;
+ do {
+ status = __raw_readl(host->base + MMC_STATUS);
+ udelay(1);
+ } while (!(status & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)));
+#ifdef CONFIG_MMC_DEBUG
+ dump_status(__FUNCTION__, status);
+#endif
+ if (status & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)) {
+ 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 = MMC_ERR_TIMEOUT;
+ __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 = MMC_ERR_BADCRC;
+ __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) {
+ pr_debug("%s: Write CRC error occurred\n",
+ DRIVER_NAME);
+ data->error = MMC_ERR_BADCRC;
+ __raw_writel(STATUS_WRITE_CRC_ERR,
+ host->base + MMC_STATUS);
+ }
+ __raw_writel(STATUS_WRITE_OP_DONE,
+ host->base + MMC_STATUS);
+ }
+ } else {
+ data->error = MMC_ERR_FAILED;
+ pr_debug("%s:%d: MXC MMC DMA transfer failed.\n", __FUNCTION__,
+ __LINE__);
+ }
+
+ mxcmci_data_done(host, status);
+}
+#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;
+ }
+ platform_set_drvdata(pdev, mmc);
+
+ mmc->ops = &mxcmci_ops;
+ mmc->ocr_avail = mmc_plat->ocr_mask;
+
+ /* Hack to work with LP1070 */
+ mmc->ocr_avail |= MMC_VDD_31_32;
+
+ mmc->max_phys_segs = NR_SG;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+
+ host = mmc_priv(mmc);
+ 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 out;
+ }
+
+ host->clk = clk_get(&pdev->dev, "sdhc_clk");
+ clk_enable(host->clk);
+
+ mmc->f_min = mmc_plat->min_clk;
+ mmc->f_max = mmc_plat->max_clk;
+ pr_debug("SDHC:%d clock:%lu\n", pdev->id, clk_get_rate(host->clk));
+
+ spin_lock_init(&host->lock);
+ host->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->res) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ 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 out;
+ }
+ host->base = (void *)IO_ADDRESS(host->res->start);
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (!host->irq) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ host->detect_irq = platform_get_irq(pdev, 1);
+ if (!host->detect_irq) {
+ goto out1;
+ }
+
+ do {
+ card_gpio_status = host->plat_data->status(host->mmc->parent);
+ if (card_gpio_status) {
+ set_irq_type(host->detect_irq, IRQT_FALLING);
+ } else {
+ set_irq_type(host->detect_irq, IRQT_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 out1;
+ }
+
+ 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 out3;
+ }
+
+ gpio_sdhc_active(pdev->id);
+
+ if ((ret = mmc_add_host(mmc)) < 0) {
+ goto out4;
+ }
+
+ printk(KERN_INFO "%s-%d found\n", pdev->name, pdev->id);
+
+ return 0;
+
+ out4:
+ gpio_sdhc_inactive(pdev->id);
+ free_irq(host->irq, host);
+ out3:
+ free_irq(host->detect_irq, host);
+ pr_debug("%s: Error in initializing....", pdev->name);
+ out1:
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+ out:
+ clk_disable(host->clk);
+ 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);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mmc) {
+ struct mxcmci_host *host = mmc_priv(mmc);
+
+ 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);
+ gpio_sdhc_inactive(pdev->id);
+ }
+ 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);
+
+ 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;
+ }
+ clk_enable(host->clk);
+
+ if (mmc) {
+ ret = mmc_resume_host(mmc);
+ host->mxc_mmc_suspend_flag = 0;
+ }
+ 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..c11f70432213
--- /dev/null
+++ b/drivers/mmc/host/mxc_mmc.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_MMC_REG_H__
+#define __MXC_MMC_REG_H__
+
+#include <asm/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_IPG_CLK_GATE_DIS (1<<15)
+#define STR_STP_CLK_IPG_PERCLK_GATE_DIS (1<<14)
+#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<<10)
+#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/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c
index 60e11a0ada97..cee88928f51d 100644
--- a/drivers/mtd/chips/cfi_probe.c
+++ b/drivers/mtd/chips/cfi_probe.c
@@ -27,7 +27,8 @@ static void print_cfi_ident(struct cfi_ident *);
static int cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi);
-static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
+static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi,
+ int amd555);
struct mtd_info *cfi_probe(struct map_info *map);
@@ -50,12 +51,12 @@ do { \
xip_allowed(base, map); \
} while (0)
-#define xip_disable_qry(base, map, cfi) \
+#define xip_disable_qry(base, map, cfi, amd555) \
do { \
xip_disable(); \
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \
+ cfi_send_gen_cmd(0x98, amd555 ? 0x555 : 0x55, base, map, cfi, cfi->device_type, NULL);\
} while (0)
#else
@@ -63,7 +64,7 @@ do { \
#define xip_disable() do { } while (0)
#define xip_allowed(base, map) do { } while (0)
#define xip_enable(base, map, cfi) do { } while (0)
-#define xip_disable_qry(base, map, cfi) do { } while (0)
+#define xip_disable_qry(base, map, cfi, amd555) do { } while (0)
#endif
@@ -102,6 +103,7 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
unsigned long *chip_map, struct cfi_private *cfi)
{
int i;
+ int amd555 = 0;
if ((base + 0) >= map->size) {
printk(KERN_NOTICE
@@ -122,14 +124,20 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
if (!qry_present(map,base,cfi)) {
- xip_enable(base, map, cfi);
- return 0;
+ cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type,
+ NULL);
+ if (!qry_present(map,base,cfi)) {
+ xip_enable(base, map, cfi);
+ return 0;
+ }
+
+ amd555 = 1;
}
if (!cfi->numchips) {
/* This is the first time we're called. Set up the CFI
stuff accordingly and return */
- return cfi_chip_setup(map, cfi);
+ return cfi_chip_setup(map, cfi, amd555);
}
/* Check each previous chip to see if it's an alias */
@@ -189,7 +197,7 @@ static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
}
static int __xipram cfi_chip_setup(struct map_info *map,
- struct cfi_private *cfi)
+ struct cfi_private *cfi, int amd555)
{
int ofs_factor = cfi->interleave*cfi->device_type;
__u32 base = 0;
@@ -214,7 +222,7 @@ static int __xipram cfi_chip_setup(struct map_info *map,
cfi->cfi_mode = CFI_MODE_CFI;
/* Read the CFI info structure */
- xip_disable_qry(base, map, cfi);
+ xip_disable_qry(base, map, cfi, amd555);
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);
diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c
index 2e51496c248e..820504bd26c6 100644
--- a/drivers/mtd/chips/cfi_util.c
+++ b/drivers/mtd/chips/cfi_util.c
@@ -50,8 +50,15 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n
local_irq_disable();
#endif
- /* Switch it into Query Mode */
- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+ /* Switch it into Query Mode. Some chips want address 0x55, some
+ * want 0x555.
+ */
+ if ((cfi->mfr == CFI_MFR_AMD) && (cfi->id == 0x227E))
+ cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type,
+ NULL);
+ else
+ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type,
+ NULL);
/* Read in the Extended Query Table */
for (i=0; i<size; i++) {
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index a592fc04cf78..cd2b70f6fc15 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -597,5 +597,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 316382a1401b..f858c14a50d1 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.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..6c69e0164c73
--- /dev/null
+++ b/drivers/mtd/maps/mxc_nor.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2004-2007 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 (nr_parts < 0 && 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 246d4512f64b..ce7e28418d39 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -291,6 +291,56 @@ config MTD_NAND_NANDSIM
The simulator may simulate various NAND flash chips for the
MTD nand layer.
+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_PLATFORM
tristate "Support for generic platform NAND driver"
depends on MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 3ad6c0165da3..ad51311061fe 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,9 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.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_AT91) += at91_nand.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
diff --git a/drivers/mtd/nand/mxc_nd.c b/drivers/mtd/nand/mxc_nd.c
new file mode 100644
index 000000000000..f244436afd16
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.c
@@ -0,0 +1,1372 @@
+/*
+ * 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/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/mtd/partitions.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/mach/flash.h>
+#include <asm/io.h>
+
+#include "mxc_nd.h"
+
+/*!
+ * Number of static partitions on NAND Flash.
+ */
+#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(struct mtd_partition))
+
+#define DVR_VER "2.0"
+
+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 = NULL;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY 2000
+/*
+ * Macros to get byte and bit positions of ECC
+ */
+#define COLPOS(x) ((x) >> 3)
+#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}}
+};
+
+/*!
+ * @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;
+ /* Workaround ecc status register error for spare-only read */
+ if (cpu_is_mxc91131_rev(CHIP_REV_2_0) >= 1) {
+ 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 = MAIN_AREA0 + (col >> 1) + (512 * buf_id);
+ } else {
+ buf = SPARE_AREA0 + (col >> 1) + (16 * 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;
+ /* Workaround ecc status register error for spare-only read */
+ if (cpu_is_mxc91131_rev(CHIP_REV_2_0) >= 1) {
+ 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 */
+
+ /* Removed NFC workaround in MXC91231-P2.1 */
+ if (cpu_is_mxc91231_rev(CHIP_REV_2_1) < 0) {
+ mxc_nd_correct_ecc(buf_id, bSpareOnly);
+ } else {
+ 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;
+
+ /*
+ * 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;
+ 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)
+ send_addr(0, false); /* another col addr cycle for 2k page */
+ }
+
+ /*
+ * Write out page address, if necessary
+ */
+ if (page_addr != -1) {
+ send_addr((page_addr & 0xff), false); /* paddr_0 - p_addr_7 */
+
+ if (is2k_Pagesize) {
+ send_addr((page_addr >> 8) & 0xFF, false);
+ if (mtd->size >= 0x42000000) {
+ send_addr((page_addr >> 16) & 0xff, true);
+ }
+ } else {
+ /* One more address cycle for higher density devices */
+ if (mtd->size >= 0x4000000) {
+ send_addr((page_addr >> 8) & 0xff, false); /* paddr_8 - paddr_15 */
+ send_addr((page_addr >> 16) & 0xff, true);
+ } else
+ send_addr((page_addr >> 8) & 0xff, true); /* paddr_8 - paddr_15 */
+ }
+ }
+
+ /*
+ * 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
+};
+
+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;
+ }
+
+ this->bbt_td = NULL;
+ this->bbt_md = NULL;
+
+ 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 = kmalloc(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(mxc_nand_data, 0, sizeof(struct mxc_mtd_s));
+ 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(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;
+
+ /* support for One Flash Clock cycle new in rev 2.0 */
+ if (cpu_is_mxc91131_rev(CHIP_REV_2_0) >= 1) {
+ NFC_CONFIG1 |= (NFC_ONE_CYCLE);
+ }
+ /* 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(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..fd15cb48cb9c
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd.h
@@ -0,0 +1,112 @@
+/*
+ * 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_nd.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND_H__
+#define __MXC_ND_H__
+
+#include <asm/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..1092248395f7
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.c
@@ -0,0 +1,1354 @@
+/*
+ * 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/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.3"
+
+/* Global address Variables */
+static u32 nfc_axi_base, nfc_ip_base;
+
+static void mxc_swap_2k_bi_main_sp(void);
+
+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 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
+
+static int page_to_block_shift;
+static int g_page_mask;
+static int scan_done;
+static int skip_erase;
+static u8 *oob_data_shadow_p;
+/*
+ * OOB data that is shadowed in the SDRAM to prevent the Spare only access
+ * to the Nand chip. This is valid only for the JFFS2 File System.
+ */
+static uint8_t *shadow_oob_data;
+
+static uint8_t oob_data_512[] = {
+ 0x85, 0x19, 0x03, 0x20, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static uint8_t oob_data_2k[] = {
+ 0xff, 0xff, 0x85, 0x19, 0x03, 0x20, 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, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+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}}
+};
+
+/*!
+ * @defgroup NAND_MTD NAND Flash MTD Driver for MXC processors
+ */
+
+/*!
+ * @file mxc_nd3.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 u8 mxc_main_xfer_buf[2048] ____cacheline_aligned;
+
+/*
+ * Functions that operate on the shadow table maintained in the RAM.
+ * Each block in the Nand chip has one bit entry in this table
+ * indicating if the block has a JFFS2 clean marker.
+ * mark_oob_data_dirty - marks a block to indicate that the block has a JFFS2
+ * clean marker
+ * is_oob_data_dirty - checks if the block has a JFFS2 clean marker
+ * mark_oob_data_clean - marks a block to indicate that the block is erased
+ * and doesnot contain JFFS2 clean marker.
+ */
+
+static void mark_oob_data_dirty(u32 page, int update_sp)
+{
+ u32 blk = page >> page_to_block_shift;
+ u32 off = blk / 8;
+ u32 bit = blk % 8;
+
+ oob_data_shadow_p[off] |= (1 << bit);
+}
+
+static int is_oob_data_dirty(u32 page)
+{
+ u32 blk = page >> page_to_block_shift;
+ u32 off = blk / 8;
+ u32 bit = blk % 8;
+
+ return oob_data_shadow_p[off] & (1 << bit);
+}
+
+static void mark_oob_data_clean(u32 page)
+{
+
+ u32 blk = page >> page_to_block_shift;
+ u32 off = blk / 8;
+ u32 bit = blk % 8;
+
+ oob_data_shadow_p[off] &= ~(1 << bit);
+}
+
+/*
+ * Functions to handle 32-bit aligned memcpy.
+ */
+static void nfc_memcpy(void *dst, const void *src, int len)
+{
+ volatile u16 *d = (volatile u16 *)dst;
+ volatile u16 *s = (volatile u16 *)src;
+ int wc;
+
+ switch ((u32) dst & 3) {
+ case 2:
+ wc = len / 2;
+ /* adjust alignment */
+ *d = *s;
+ memcpy((void *)(d + 1), (const void *)(s + 1), len - 4);
+ *(d + wc - 1) = *(s + wc - 1);
+ break;
+
+ case 1:
+ case 3:
+ memcpy((void *)mxc_main_xfer_buf, (const void *)src,
+ (len + 3) & (~3));
+ memcpy((void *)d, (const void *)mxc_main_xfer_buf, len);
+ break;
+ case 0:
+ memcpy((void *)d, (const void *)s, len);
+ }
+}
+
+/*!
+ * 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));
+ raw_write((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) {
+ raw_write((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",
+ __FUNCTION__);
+ }
+ }
+}
+
+/*!
+ * 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);
+
+ raw_write(cmd, REG_NFC_FLASH_CMD);
+ ACK_OPS;
+ raw_write(NFC_CMD, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 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 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);
+ raw_write((addr << NFC_FLASH_ADDR_SHIFT), REG_NFC_FLASH_ADDR);
+
+ ACK_OPS; /* defined only for V3 */
+ 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 (0-3)
+ */
+static void send_prog_page(u8 buf_id)
+{
+ u32 val = buf_id;
+ DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+ NFC_SET_RBA(val, RBA_BUFFER0); /* defined only for V3 */
+
+ /* Set RBA bits for BUFFER val */
+ raw_write(val, REG_NFC_SET_RBA);
+
+ ACK_OPS; /* defined only for V3 */
+ raw_write(NFC_INPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, true);
+}
+
+/*!
+ * 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 (0-3)
+ */
+static void send_read_page(u8 buf_id)
+{
+ u32 val = buf_id;
+ DEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+ NFC_SET_RBA(val, RBA_BUFFER0); /* defined only for V3 */
+ /* Set RBA bits for BUFFER val */
+ raw_write(val, REG_NFC_SET_RBA);
+
+ ACK_OPS; /* defined only for V3 */
+ raw_write(NFC_OUTPUT, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, true);
+}
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+ u32 val = 0;
+
+ /* NFC buffer 0 is used for device ID output */
+ /* Set RBA bits for BUFFER0 */
+
+ NFC_SET_RBA(val, RBA_BUFFER0); /* defined only for V3 */
+ raw_write(val, REG_NFC_SET_RBA);
+
+ ACK_OPS; /* defined only for V3 */
+ /* Read ID into main buffer */
+ raw_write(NFC_ID, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, true);
+
+}
+
+/*!
+ * 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)
+{
+ volatile u16 *mainBuf = MAIN_AREA1;
+ volatile u32 store;
+ u32 val = 1;
+ u16 ret;
+ /* Issue status request to NAND device */
+
+ /* store the main area1 first word, later do recovery */
+ store = *((u32 *) mainBuf);
+ *(u32 *) mainBuf = 0x0;
+
+ /*
+ * NFC buffer 1 is used for device status to prevent
+ * corruption of read/write buffer on status requests.
+ */
+
+ /* Set RBA bits for BUFFER1 */
+ NFC_SET_RBA(val, RBA_BUFFER1); /* defined only for V3 */
+ raw_write(val, REG_NFC_SET_RBA);
+
+ ACK_OPS; /* defined only for V3 */
+ /* Read status into main buffer */
+ raw_write(NFC_STATUS, REG_NFC_OPS);
+
+ /* Wait for operation to complete */
+ wait_op_done(TROP_US_DELAY, 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)
+{
+ /*
+ * For V1/V2 NFC this function returns always 1.
+ */
+ if (CHECK_NFC_RB)
+ return 1;
+ else
+ return 0;
+}
+
+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)
+{
+ u16 ecc_stat, err;
+ int no_subpages = 1;
+ int ret = 0;
+
+ if (IS_2K_PAGE_NAND) {
+ no_subpages = 4;
+ }
+
+ ecc_stat = raw_read(REG_NFC_ECC_STATUS_RESULT);
+ do {
+ err = ecc_stat & 0x7;
+ if (err > 0x4) {
+ return -1;
+ } else {
+ ret += err;
+ }
+ ecc_stat >>= 4;
+ } while (--no_subpages);
+
+ return ret;
+}
+
+/*
+ * Function to correct the detected errors. This NFC corrects all the errors
+ * detected. So this function is not required.
+ */
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char * dat,
+ u_char * read_ecc, u_char * calc_ecc)
+{
+ panic("Shouldn't be called here: %d\n", __LINE__);
+ return 0; //FIXME
+}
+
+/*
+ * 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
+ * 4 symbols of 9 bits each in 528 byte page.
+ * So this function is not required.
+ */
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
+ u_char * ecc_code)
+{
+ panic(KERN_ERR "Shouldn't be called here %d \n", __LINE__);
+ return 0; //FIXME
+}
+
+/*!
+ * 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, rdWord;
+ volatile u16 *mainBuf = MAIN_AREA0;
+ volatile u16 *spareBuf = SPARE_AREA0;
+
+ /* 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];
+ }
+
+ /* Update saved column address */
+ g_nandfc_info.colAddr += 2;
+
+ return rdWord;
+}
+
+/*!
+ * 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)
+{
+ volatile uint32_t *base;
+ panic("re-work needed\n");
+ if (g_nandfc_info.colAddr >= mtd->writesize || g_nandfc_info.bSpareOnly) {
+ base = (uint32_t *) SPARE_AREA0;
+ } else {
+ g_nandfc_info.colAddr += len;
+ base = (uint32_t *) MAIN_AREA0;
+ }
+ memcpy((void *)base, (void *)buf, len);
+}
+
+/*!
+ * 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)
+{
+ volatile uint32_t *base;
+
+ if (g_nandfc_info.colAddr >= mtd->writesize || g_nandfc_info.bSpareOnly) {
+ base = (uint32_t *) SPARE_AREA0;
+ } else {
+ base = (uint32_t *) MAIN_AREA0;
+ g_nandfc_info.colAddr += len;
+ }
+ nfc_memcpy((void *)buf, (void *)base, len);
+}
+
+/*!
+ * 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)
+{
+ volatile u32 *mainBuf = (u32 *) MAIN_AREA0;
+ /* check for 32-bit alignment? */
+ uint32_t *p = (uint32_t *) buf;
+ if (IS_2K_PAGE_NAND)
+ mxc_swap_2k_bi_main_sp();
+ for (; len > 0; len -= 4) {
+ if (*p++ != *mainBuf++) {
+ 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)
+{
+#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) {
+ raw_write((raw_read(REG_NFC_CE) & ~NFC_CE), REG_NFC_CE);
+ return;
+ }
+
+ raw_write((raw_read(REG_NFC_CE) | NFC_CE), REG_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;
+ }
+}
+
+/*
+ * Function to perform the address cycles.
+ */
+static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
+{
+ 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);
+ }
+ }
+ if (page_addr != -1) {
+ do {
+ send_addr((page_addr & 0xff), false);
+ page_mask >>= 8;
+ page_addr >>= 8;
+ } while (page_mask != 0);
+ }
+
+}
+
+/*
+ * Function to read a page from nand device.
+ */
+static void read_full_page(struct mtd_info *mtd, int page_addr)
+{
+ send_cmd(NAND_CMD_READ0, false);
+
+ mxc_do_addr_cycle(mtd, 0, page_addr);
+
+ if (IS_2K_PAGE_NAND) {
+ send_cmd(NAND_CMD_READSTART, false);
+ READ_2K_PAGE;
+ mxc_swap_2k_bi_main_sp();
+ } else {
+ send_read_page(0);
+ }
+}
+
+/*
+ * Function to check if the page read is a clean page.(Valid only
+ * the first page of the block.
+ * It is done by checking if all spare data of the page is all 0xFF.
+ * This is valid even if ECC generated is all 0xFF as the JFFS2 places
+ * clean marker bytes in the first page of each block which is non 0xFF.
+ */
+static int is_page_clean(struct mtd_info *mtd)
+{
+ volatile u32 *p = (u32 *) SPARE_AREA0;
+ int len;
+
+ /*Check spare page */
+ len = mtd->oobsize;
+ for (; len > 0; len -= 4) {
+ if (*p++ != 0xFFFFFFFF) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*!
+ * 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.bSpareOnly = false;
+ useirq = false;
+ break;
+
+ case NAND_CMD_READOOB:
+ g_nandfc_info.colAddr = column;
+ g_nandfc_info.bSpareOnly = true;
+ useirq = false;
+ command = NAND_CMD_READ0; /* only READ0 is valid */
+ break;
+
+ case NAND_CMD_SEQIN:
+ if (column >= mtd->writesize) {
+ g_nandfc_info.bSpareOnly = true;
+ mark_oob_data_dirty(page_addr, 1);
+ } else {
+ if (is_oob_data_dirty(page_addr)) {
+ memcpy((void *)SPARE_AREA0, shadow_oob_data,
+ mtd->oobsize);
+ } else {
+ memset((void *)SPARE_AREA0, 0xFF, mtd->oobsize);
+ }
+ g_nandfc_info.bSpareOnly = false;
+ /* Set program pointer to page start */
+ send_cmd(NAND_CMD_READ0, false);
+ }
+ useirq = false;
+ break;
+
+ case NAND_CMD_PAGEPROG:
+ if (!g_nandfc_info.bSpareOnly) {
+ if (IS_2K_PAGE_NAND) {
+ PROG_2K_PAGE} else {
+ send_prog_page(0);
+ }
+ } else {
+ return;
+ }
+ break;
+
+ case NAND_CMD_ERASE1:
+ /*Decide to erase */
+ read_full_page(mtd, page_addr);
+ if (is_page_clean(mtd)) {
+ mark_oob_data_clean(page_addr);
+ skip_erase = 1;
+ return;
+ }
+ useirq = false;
+ break;
+ case NAND_CMD_ERASE2:
+ if (skip_erase) {
+ skip_erase = 0;
+ return;
+ }
+ useirq = false;
+ break;
+ }
+
+ /*
+ * Write out the command to the device.
+ */
+ send_cmd(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_2K_PAGE_NAND) {
+ /* send read confirm command */
+ send_cmd(NAND_CMD_READSTART, true);
+ /* read for each AREA */
+ READ_2K_PAGE;
+ } else {
+ send_read_page(0);
+ }
+ break;
+
+ case NAND_CMD_READID:
+ send_read_id();
+ break;
+ }
+}
+
+#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);
+ }
+ }
+
+}
+#else
+#define mxc_low_erase(x)
+#endif
+
+/* Kevin: why do we need this???,
+ * Yes, to avoid LED event trigger functions which will add code, -Raj*/
+
+static int mxc_nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ unsigned long timeo = jiffies;
+ int status, state = chip->state;
+
+ if (state == FL_ERASING)
+ timeo += (HZ * 400) / 1000;
+ else
+ timeo += (HZ * 20) / 1000;
+
+ send_cmd(NAND_CMD_STATUS, 1);
+
+ while (time_before(jiffies, timeo)) {
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3
+ if (chip->dev_ready) {
+ if (chip->dev_ready(mtd))
+ break;
+ } else
+#endif
+ {
+ if (get_dev_status() & NAND_STATUS_READY)
+ break;
+ }
+ cond_resched();
+ }
+
+ status = (int)(get_dev_status());
+ return status;
+}
+
+static int mxc_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page, int sndcmd)
+{
+ if (scan_done && is_oob_data_dirty(page)) {
+ memcpy((void *)chip->oob_poi, (void *)shadow_oob_data,
+ mtd->oobsize);
+ return 0;
+ }
+
+ if (sndcmd) {
+ read_full_page(mtd, page);
+ sndcmd = 0;
+ }
+
+ nfc_memcpy((void *)chip->oob_poi, (void *)SPARE_AREA0, mtd->oobsize);
+ return sndcmd;
+}
+
+static int mxc_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ const uint8_t *buf = chip->oob_poi;
+ int read_oob_col = 0;
+ volatile uint16_t *p_addr = SPARE_AREA0;
+
+ //FIXME Check for bad block marking
+ if (0xFF == buf[chip->badblockpos]) {
+ mark_oob_data_dirty(page, 1);
+ } else {
+ send_cmd(NAND_CMD_READ0, false);
+ send_cmd(NAND_CMD_SEQIN, false);
+ mxc_do_addr_cycle(mtd, read_oob_col, page);
+
+ memcpy((void *)p_addr, buf, mtd->oobsize);
+ /* Send command to program the OOB data */
+ send_prog_page(0);
+ send_cmd(NAND_CMD_PAGEPROG, true);
+
+ status = mxc_nand_wait(mtd, chip);
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * This function does the trick of swapping the 464th byte in the last RAM
+ * buffer in the main area with the 0th byte in the spare area. This seems
+ * to be the optimal way of addressing the NFC imcompatibility problem with
+ * the NAND flash out of factory in terms of BI field.
+ * Note: this function only operates on the NFC's internal RAM buffers and
+ * for 2K page only.
+ */
+static void mxc_swap_2k_bi_main_sp(void)
+{
+ u16 tmp1, tmp2, new_tmp1;
+
+ tmp1 = __raw_readw(BAD_BLK_MARKER_464);
+ tmp2 = __raw_readw(BAD_BLK_MARKER_SP_0);
+ new_tmp1 = (tmp1 & 0xFF00) | (tmp2 >> 8);
+ tmp2 = (tmp1 << 8) | (tmp2 & 0xFF);
+ __raw_writew(new_tmp1, BAD_BLK_MARKER_464);
+ __raw_writew(tmp2, BAD_BLK_MARKER_SP_0);
+
+}
+
+/* Kevin: This is solid but need to optimize the nfc_memcpy */
+static int mxc_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t * buf)
+{
+ int stat;
+
+ stat = mxc_check_ecc_status(mtd);
+ if (stat == -1) {
+ mtd->ecc_stats.failed++;
+ printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ if (stat)
+ pr_debug("%d Symbol Correctable RS-ECC Error\n", stat);
+ }
+
+ if (IS_2K_PAGE_NAND) {
+ mxc_swap_2k_bi_main_sp();
+ }
+
+ nfc_memcpy((void *)buf, (void *)MAIN_AREA0, mtd->writesize);
+
+ return 0;
+}
+
+/* Kevin: This is clean and solid */
+static void mxc_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t * buf)
+{
+ memcpy((void *)MAIN_AREA0, buf, mtd->writesize);
+
+ if (IS_2K_PAGE_NAND) {
+ mxc_swap_2k_bi_main_sp();
+ }
+}
+
+/* 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
+};
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+ /* Do some configurations before scanning */
+ page_to_block_shift = this->phys_erase_shift - this->page_shift;
+ g_page_mask = this->pagemask;
+
+ if (IS_2K_PAGE_NAND) {
+ NFMS |= (1 << NFMS_NF_PG_SZ);
+ this->ecc.layout = &nand_hw_eccoob_2k;
+ shadow_oob_data = oob_data_2k;
+ } else {
+ this->ecc.layout = &nand_hw_eccoob_512;
+ shadow_oob_data = oob_data_512;
+ }
+
+ /* propagate ecc.layout to mtd_info */
+ mtd->ecclayout = this->ecc.layout;
+
+ this->bbt_td = NULL;
+ this->bbt_md = NULL;
+ 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);
+}
+
+/*!
+ * 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, n, err = 0;
+
+ nfc_axi_base = IO_ADDRESS(NFC_AXI_BASE_ADDR);
+ nfc_ip_base = IO_ADDRESS(NFC_BASE_ADDR);
+
+ /* Resetting NFC */
+ raw_write(NFC_RST, REG_NFC_RST);
+
+ /* 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;
+
+ /* 5 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->waitfunc = mxc_nand_wait;
+ 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;
+ NFMS |= (1 << NFMS_NF_DWIDTH);
+ }
+
+ nfc_clk = clk_get(&pdev->dev, "nfc_clk");
+ clk_enable(nfc_clk); /* Enabled here to satisfy following reset command to succeed */
+
+ /* Disable interrupt */
+ raw_write((raw_read(REG_NFC_INTRRUPT) | NFC_INT_MSK), REG_NFC_INTRRUPT);
+
+ init_waitqueue_head(&irq_waitq);
+ err = request_irq(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.write_oob = mxc_nand_write_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; /* RS-ECC is applied for both MAIN+SPARE not MAIN alone */
+ this->ecc.bytes = 9; /* used for both main and spare area */
+ 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);
+ }
+
+ raw_write(raw_read(REG_NFC_SP_EN) & ~NFC_SP_EN, REG_NFC_SP_EN);
+
+ /* Reset NAND */
+ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+ /* preset operation */
+ /* Unlock the internal RAM Buffer */
+ raw_write(NFC_SET_BLS(NFC_BLS_UNLCOKED), REG_NFC_BLS);
+
+ /* Blocks to be unlocked */
+ /* Start Address = 0X0, End Address = 0xFFFF */
+ UNLOCK_ADDR(0x0, 0xFFFF);
+
+ /* Unlock Block Command for given address range */
+ raw_write(NFC_SET_WPC(NFC_WPC_UNLOCK), REG_NFC_WPC);
+
+ /* 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;
+ }
+ scan_done = 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);
+ }
+
+ platform_set_drvdata(pdev, mtd);
+
+ n = mtd->size / mtd->erasesize;
+ /* each bit is used for one page's dirty information */
+ oob_data_shadow_p = (u8 *) kzalloc(n / 8, GFP_KERNEL);
+ if (!oob_data_shadow_p) {
+ printk(KERN_ERR "%s: failed to allocate oob_data_shadow_p\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Erase all the blocks of a NAND -- depend on the config */
+ mxc_low_erase(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_disable(nfc_clk);
+ clk_put(nfc_clk);
+ platform_set_drvdata(pdev, NULL);
+
+ if (mxc_nand_data) {
+ nand_release(mtd);
+ free_irq(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_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-3");
+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..174afdee9ebd
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nd2.h
@@ -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 mxc_nd.h
+ *
+ * @brief This file contains the NAND Flash Controller register information.
+ *
+ *
+ * @ingroup NAND_MTD
+ */
+
+#ifndef __MXC_ND2_H__
+#define __MXC_ND2_H__
+
+#include <asm/hardware.h>
+
+#define IS_2K_PAGE_NAND (mtd->writesize == NAND_PAGESIZE_2KB)
+#define NAND_PAGESIZE_2KB NAND_MAX_PAGESIZE
+
+#ifdef CONFIG_ARCH_MXC_HAS_NFC_V3
+/*
+ * For V3 NFC registers Definition
+ */
+
+/* AXI Bus Mapped */
+#define NFC_AXI_BASE_ADDR NFC_BASE_ADDR_AXI
+#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)
+
+/* IP Bus Mapped */
+#define NFC_WRPROT (nfc_ip_base + 0x00)
+#define NFC_WRPROT_UNLOCK_BLK_ADD0 (nfc_ip_base + 0x04)
+#define NFC_WRPROT_UNLOCK_BLK_ADD1 (nfc_ip_base + 0x08)
+#define NFC_WRPROT_UNLOCK_BLK_ADD2 (nfc_ip_base + 0x0C)
+#define NFC_WRPROT_UNLOCK_BLK_ADD3 (nfc_ip_base + 0x10)
+#define NFC_CONFIG2 (nfc_ip_base + 0x14)
+#define NFC_IPC (nfc_ip_base + 0x18)
+#define NFC_AXI_ERR_ADD (nfc_ip_base + 0x1C)
+
+/*!
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x000)
+#define MAIN_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x200)
+#define MAIN_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x400)
+#define MAIN_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x600)
+
+/*!
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#define SPARE_AREA0 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x800)
+#define SPARE_AREA1 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x810)
+#define SPARE_AREA2 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x820)
+#define SPARE_AREA3 (volatile u16 *)IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x830)
+
+/* read column 464-465 byte but only 464 for bad block marker */
+#define BAD_BLK_MARKER_464 IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x600 + 464)
+/* read column 0-1 byte, but only 1 is used for swapped main area data */
+#define BAD_BLK_MARKER_SP_0 IO_ADDRESS(NFC_BASE_ADDR_AXI + 0x800)
+
+/*!
+ * 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 << 31)
+#define NFC_INT_MSK (1 << 4)
+#define NFC_BIG (1 << 5)
+#define NFC_FLASH_ADDR_SHIFT 16
+#define NFC_UNLOCK_END_ADDR_SHIFT 16
+#define RBA_BUFFER0 (0 << 4)
+#define RBA_BUFFER1 (1 << 4)
+#define RBA_BUFFER2 (2 << 4)
+#define RBA_BUFFER3 (3 << 4)
+#define RBA_RESET ~(3 << 4)
+#define NFC_RB (1 << 29)
+#define NFC_ECC_EN (1 << 3)
+#define NFC_CE (1 << 1)
+#define NFC_RST (1 << 6)
+#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)
+#define NFC_SP_EN (1)
+#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)
+#define NFC_WPC_LOCK_TIGHT (1)
+#define NFC_WPC_LOCK (1 << 1)
+#define NFC_WPC_UNLOCK (1 << 2)
+#define NFC_WPC_RESET ~(7)
+
+/* NFC Register Mapping */
+#define REG_NFC_OPS_STAT NFC_IPC
+#define REG_NFC_INTRRUPT NFC_CONFIG2
+#define REG_NFC_FLASH_ADDR NFC_FLASH_ADDR_CMD
+#define REG_NFC_FLASH_CMD NFC_FLASH_ADDR_CMD
+#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_CONFIG2
+#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
+
+/* 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);
+
+/* NFC buffer 0 to 3 are used for page read/write, starting with buffer0 */
+/* Set RBA bits for BUFFER0 */
+#define NFC_SET_RBA(val, buf_id) \
+ val = ((raw_read(REG_NFC_SET_RBA) & RBA_RESET) | buf_id);
+
+#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_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
+
+#define READ_2K_PAGE send_read_page(0);
+#define PROG_2K_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
+ */
+#define NFC_BUF_SIZE (nfc_ip_base + 0xE00)
+#define NFC_BUF_ADDR (nfc_ip_base + 0xE04)
+#define NFC_FLASH_ADDR (nfc_ip_base + 0xE06)
+#define NFC_FLASH_CMD (nfc_ip_base + 0xE08)
+#define NFC_CONFIG (nfc_ip_base + 0xE0A)
+#define NFC_ECC_STATUS_RESULT (nfc_ip_base + 0xE0C)
+#define NFC_RSLTMAIN_AREA (nfc_ip_base + 0xE0E)
+#define NFC_RSLTSPARE_AREA (nfc_ip_base + 0xE10)
+#define NFC_WRPROT (nfc_ip_base + 0xE12)
+#define NFC_UNLOCKSTART_BLKADDR (nfc_ip_base + 0xE14)
+#define NFC_UNLOCKEND_BLKADDR (nfc_ip_base + 0xE16)
+#define NFC_NF_WRPRST (nfc_ip_base + 0xE18)
+#define NFC_CONFIG1 (nfc_ip_base + 0xE1A)
+#define NFC_CONFIG2 (nfc_ip_base + 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)
+
+/* read column 464-465 byte but only 464 for bad block marker */
+#define BAD_BLK_MARKER_464 IO_ADDRESS(NFC_BASE_ADDR + 0x600 + 464)
+/* read column 0-1 byte, but only 1 is used for swapped main area data */
+#define BAD_BLK_MARKER_SP_0 IO_ADDRESS(NFC_BASE_ADDR + 0x800)
+
+/*!
+ * 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
+
+/* 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
+
+/* 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_WPC(val) val
+
+/* NULL Definitions */
+#define ACK_OPS
+#define NFC_SET_RBA(val,buf_id)
+
+#define READ_2K_PAGE send_read_page(0);\
+ send_read_page(1);\
+ send_read_page(2);\
+ send_read_page(3);
+
+#define PROG_2K_PAGE send_prog_page(0);\
+ send_prog_page(1);\
+ send_prog_page(2);\
+ send_prog_page(3);
+
+#define CHECK_NFC_RB 1
+
+#endif
+
+#endif /* MXCND_H */
diff --git a/drivers/mxc/Kconfig b/drivers/mxc/Kconfig
new file mode 100644
index 000000000000..f913e03af789
--- /dev/null
+++ b/drivers/mxc/Kconfig
@@ -0,0 +1,18 @@
+# drivers/video/mxc/Kconfig
+
+if ARCH_MXC
+
+menu "MXC support drivers"
+
+source "drivers/mxc/ipu/Kconfig"
+source "drivers/mxc/ssi/Kconfig"
+source "drivers/mxc/dam/Kconfig"
+source "drivers/mxc/pmic/Kconfig"
+source "drivers/mxc/pm/Kconfig"
+source "drivers/mxc/security/Kconfig"
+source "drivers/mxc/hmp4e/Kconfig"
+source "drivers/mxc/vpu/Kconfig"
+
+endmenu
+
+endif
diff --git a/drivers/mxc/Makefile b/drivers/mxc/Makefile
new file mode 100644
index 000000000000..4a7029df5200
--- /dev/null
+++ b/drivers/mxc/Makefile
@@ -0,0 +1,11 @@
+obj-$(CONFIG_MXC_IPU) += ipu/
+obj-$(CONFIG_MXC_SSI) += ssi/
+obj-$(CONFIG_MXC_DAM) += dam/
+
+obj-$(CONFIG_MXC_PMIC) += pmic/
+
+obj-$(CONFIG_MXC_DPTC) += pm/
+obj-$(CONFIG_MX27_DPTC) += pm/
+obj-$(CONFIG_MXC_HMP4E) += hmp4e/
+obj-y += security/
+obj-$(CONFIG_MXC_VPU) += vpu/
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..54c7dbc78625
--- /dev/null
+++ b/drivers/mxc/dam/dam.c
@@ -0,0 +1,427 @@
+/*
+ * 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 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 <asm/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..ee5575213e65
--- /dev/null
+++ b/drivers/mxc/dam/dam_v1.c
@@ -0,0 +1,616 @@
+/*
+ * 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 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 <asm/uaccess.h>
+#include "dam.h"
+
+/*!
+ * This include to define bool type, false and true definitions.
+ */
+#include <asm/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 class_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 = 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");
+ class_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
+ class_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/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..459d2f34a3e0
--- /dev/null
+++ b/drivers/mxc/hmp4e/mxc_hmp4e.c
@@ -0,0 +1,811 @@
+/*
+ * 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)
+ *
+ * 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 = 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 class_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 = 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)
+{
+ class_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/ipu/Kconfig b/drivers/mxc/ipu/Kconfig
new file mode 100644
index 000000000000..c0b61e1db562
--- /dev/null
+++ b/drivers/mxc/ipu/Kconfig
@@ -0,0 +1,19 @@
+menu "MXC IPU"
+
+config MXC_IPU
+ bool "MXC Image Processing Unit"
+ depends on ARCH_MXC
+ depends on !ARCH_MX21
+ depends on !ARCH_MX27
+ help
+ If you plan to use the Image Processing unit in the MXC, say
+ Y here. If unsure, select Y.
+
+config MXC_IPU_LPMC
+ bool
+ depends on MXC_IPU
+ default y if ARCH_MXC91231
+
+source "drivers/mxc/ipu/pf/Kconfig"
+
+endmenu
diff --git a/drivers/mxc/ipu/Makefile b/drivers/mxc/ipu/Makefile
new file mode 100644
index 000000000000..f94f0eb64dca
--- /dev/null
+++ b/drivers/mxc/ipu/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_MXC_IPU) += mxc_ipu.o
+
+mxc_ipu-objs := ipu_common.o ipu_sdc.o ipu_adc.o ipu_ic.o ipu_csi.o ipu_device.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..85a53688b06e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_adc.c
@@ -0,0 +1,677 @@
+/*
+ * 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_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 <asm/io.h>
+#include <asm/arch/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;
+ }
+}
+
+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_common.c b/drivers/mxc/ipu/ipu_common.c
new file mode 100644
index 000000000000..8de829573540
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_common.c
@@ -0,0 +1,1804 @@
+/*
+ * 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_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 <asm/io.h>
+#include <asm/arch/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);
+
+ __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(0x00000070L, 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");
+ break;
+ }
+
+ /* 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;
+ uint32_t stride_bytes;
+
+ dma_chan = channel_2_dma(channel, type);
+ stride_bytes = stride * bytes_per_pixel(pixel_fmt);
+
+ if (dma_chan == IDMA_CHAN_INVALID)
+ return -EINVAL;
+
+ if (stride_bytes % 4) {
+ dev_err(g_ipu_dev,
+ "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n",
+ stride, stride_bytes);
+ return -EINVAL;
+ }
+ /* IC channels' stride must be multiple of 8 pixels */
+ if ((dma_chan <= 13) && (stride % 8)) {
+ dev_err(g_ipu_dev, "Stride 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_bytes,
+ 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 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 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);
+
+ /* Reset to buffer 0 */
+ __raw_writel(chan_mask, IPU_CHA_CUR_BUF);
+
+ /* 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;
+}
+
+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;
+ }
+ }
+ 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;
+ }
+ }
+ 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..ece04f003f85
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_csi.c
@@ -0,0 +1,217 @@
+/*
+ * 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_csi.c
+ *
+ * @brief IPU CMOS Sensor interface functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <asm/arch/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
+ *
+ */
+void ipu_csi_get_window_size(uint32_t * width, uint32_t * height)
+{
+ 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
+ *
+ */
+void ipu_csi_set_window_size(uint32_t width, uint32_t height)
+{
+ __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
+ *
+ */
+void ipu_csi_set_window_pos(uint32_t left, uint32_t top)
+{
+ 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..c777d143146b
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_device.c
@@ -0,0 +1,600 @@
+/*
+ * 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_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 <asm/io.h>
+#include <asm/arch/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;
+
+typedef struct _event_type {
+ int irq;
+ void *dev;
+} event_type;
+
+event_type events[MAX_Q_SIZE];
+
+int register_ipu_device(void);
+
+/* Static functions */
+
+int get_events(event_type * p)
+{
+ unsigned long flags;
+ int ret = 0;
+ spin_lock_irqsave(&queue_lock, flags);
+ if (pending_events != 0) {
+ *p = events[read_ptr];
+ read_ptr++;
+ pending_events--;
+ if (read_ptr >= MAX_Q_SIZE)
+ read_ptr = 0;
+ } else {
+ ret = -1;
+ }
+
+ spin_unlock_irqrestore(&queue_lock, flags);
+ return ret;
+}
+
+static irqreturn_t mxc_ipu_generic_handler(int irq, void *dev_id)
+{
+ event_type 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_sdc_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 */
+ {
+ event_type ev;
+ int r = -1;
+ r = get_events(&ev);
+ if (r == -1) {
+ wait_event_interruptible(waitq,
+ (pending_events != 0));
+ r = get_events(&ev);
+ }
+ ret = -1;
+ if (r == 0) {
+ if (!copy_to_user((event_type *) arg, &ev,
+ sizeof(event_type))) {
+ 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;
+ ipu_csi_get_window_size(&w.width, &w.height);
+ 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;
+ if (copy_from_user
+ (&w, (ipu_csi_window_size *) arg, sizeof(w)))
+ return -EFAULT;
+ ipu_csi_set_window_size(w.width, w.height);
+ }
+ break;
+ case IPU_CSI_SET_WINDOW:
+ {
+ ipu_csi_window p;
+ if (copy_from_user
+ (&p, (ipu_csi_window *) arg, sizeof(p)))
+ return -EFAULT;
+ ipu_csi_set_window_pos(p.left, p.top);
+ }
+ 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;
+ default:
+ break;
+
+ }
+ return ret;
+}
+
+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,
+ .release = mxc_ipu_release,
+ .ioctl = mxc_ipu_ioctl
+};
+
+int register_ipu_device()
+{
+ int ret = 0;
+ struct class_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 =
+ class_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..83fc349deb10
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_ic.c
@@ -0,0 +1,579 @@
+/*
+ * 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_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 <asm/io.h>
+#include <asm/arch/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);
+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 (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..1da41734f36e
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_prv.h
@@ -0,0 +1,59 @@
+/*
+ * 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_PRV_H__
+#define __INCLUDE_IPU_PRV_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <asm/arch/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..6feb24d63cfd
--- /dev/null
+++ b/drivers/mxc/ipu/ipu_sdc.c
@@ -0,0 +1,355 @@
+/*
+ * 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_sdc.c
+ *
+ * @brief IPU SDC submodule API functions
+ *
+ * @ingroup IPU
+ */
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/arch/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 vSyncWidth, 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 ((vSyncWidth == 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_start_width + h_end_width - 1) << 16);
+ __raw_writel(reg, SDC_HOR_CONF);
+
+ reg = ((uint32_t) (vSyncWidth - 1) << 26) | SDC_V_SYNC_WIDTH_L |
+ ((uint32_t) (height + v_start_width + v_end_width - 1) << 16);
+ __raw_writel(reg, SDC_VER_CONF);
+
+ g_h_start_width = h_start_width;
+ g_v_start_width = v_start_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_sdc_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_sdc_set_window_pos);
diff --git a/drivers/mxc/ipu/pf/Kconfig b/drivers/mxc/ipu/pf/Kconfig
new file mode 100644
index 000000000000..02cda917fb7a
--- /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
+ 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..bb321cbcd68b
--- /dev/null
+++ b/drivers/mxc/ipu/pf/mxc_pf.c
@@ -0,0 +1,1000 @@
+/*
+ * 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 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 <asm/arch/ipu.h>
+#include <asm/arch/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;
+ bool buffer_dirty;
+ 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.buffer_dirty = true;
+ 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)
+{
+ if (pf_data.buffer_dirty) {
+ flush_cache_all();
+ outer_flush_all();
+ }
+ pf_data.buffer_dirty = false;
+
+ 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 class_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 = 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) {
+ class_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/pm/Kconfig b/drivers/mxc/pm/Kconfig
new file mode 100644
index 000000000000..94715858ab51
--- /dev/null
+++ b/drivers/mxc/pm/Kconfig
@@ -0,0 +1,39 @@
+#
+# Power Managment devices
+#
+
+menu "Advanced Power Management devices"
+
+config MXC_DPTC
+ bool "MXC DPTC driver"
+ depends on (ARCH_MX3 || ARCH_MXC91321) && MXC_PMIC_MC13783
+ default y
+ help
+ This selects the Freescale MXC Internal DPTC driver.
+ If unsure, say N.
+
+config MX27_DPTC
+ bool "MXC DPTC driver"
+ depends on ARCH_MX27 && MXC_PMIC_MC13783
+ default y
+ help
+ This selects the Freescale MX27 Internal DPTC driver.
+ If unsure, say N.
+
+config MXC_DVFS
+ bool "MXC DVFS driver"
+ depends on ARCH_MX3
+ default y
+ help
+ This selects the Freescale MXC Internal DVFS driver.
+ If unsure, say N.
+
+config MXC_DVFS_SDMA
+ bool "MXC DVFS SDMA support"
+ depends on MXC_DVFS
+ default n
+ help
+ This selects the Freescale MXC Internal DVFS driver SDMA support.
+ If unsure, say N.
+
+endmenu
diff --git a/drivers/mxc/pm/Makefile b/drivers/mxc/pm/Makefile
new file mode 100644
index 000000000000..6395fbac3e64
--- /dev/null
+++ b/drivers/mxc/pm/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for Power Managment devices.
+#
+
+# Module dependencies for the MXC driver
+obj-$(CONFIG_MXC_DPTC) += dptc.o
+
+obj-$(CONFIG_MXC_DPTC) += dvfs_dptc.o
+
+obj-$(CONFIG_MXC_DVFS) += dvfs_dptc.o
+
+obj-$(CONFIG_MX27_DPTC) += dptc_mx27.o
diff --git a/drivers/mxc/pm/dptc.c b/drivers/mxc/pm/dptc.c
new file mode 100644
index 000000000000..9e6bc5c3f92b
--- /dev/null
+++ b/drivers/mxc/pm/dptc.c
@@ -0,0 +1,994 @@
+/*
+ * 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 dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DPTC module.
+ *
+ * The DPTC driver
+ * driver is designed as a character driver which interacts with the MXC DPTC
+ * 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 voltage, increase voltage or emergency) and changes
+ * the CPU voltage according to translation table that is loaded into the driver
+ * (the voltage changes are done by calling some routines in the mc13783 driver).
+ * The driver read method is used to read the currently loaded DPTC translation
+ * table and the write method is used in-order to update the translation table.
+ * Driver ioctls are used to change driver parameters and enable/disable the
+ * DPTC operation.
+ *
+ * @ingroup PM_MXC91321 PM_MX31
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/semaphore.h>
+#include <asm/arch/sdma.h>
+#include <asm/arch/pmic_power.h>
+
+/*
+ * Module header file
+ */
+#include <asm/arch/dptc.h>
+
+/*
+ * CRM registers
+ */
+#ifdef CONFIG_ARCH_MX3
+#include "../../../arch/arm/mach-mx3/crm_regs.h"
+#endif
+#ifdef CONFIG_ARCH_MXC91321
+#include "../../../arch/arm/mach-mxc91321/crm_regs.h"
+#endif
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s *dvfs_dptc_params;
+
+static struct delayed_work dptc_work;
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+static unsigned long ptvai;
+#endif
+
+/*!
+ * Spinlock to protect CRM register accesses
+ */
+static DEFINE_SPINLOCK(mxc_crm_lock);
+
+/*!
+ * This function is called to read the contents of a CCM_MCU register
+ *
+ * @param reg_offset the CCM_MCU register that will read
+ *
+ * @return the register contents
+ */
+static unsigned long mxc_ccm_get_reg(unsigned int reg_offset)
+{
+ unsigned long reg;
+
+ reg = __raw_readl(reg_offset);
+ return reg;
+}
+
+/*!
+ * 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
+ */
+static void mxc_ccm_modify_reg(unsigned int 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);
+}
+
+/*!
+ * Enable DPTC hardware
+ */
+static void dptc_enable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN,
+ MXC_CCM_PMCR0_DPTEN);
+}
+
+/*!
+ * Disable DPTC hardware
+ */
+static void dptc_disable_dptc(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPTEN, 0);
+}
+
+/*!
+ * Mask DPTC interrupt
+ */
+static void dptc_mask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM,
+ MXC_CCM_PMCR0_PTVAIM);
+}
+
+/*!
+ * Unmask DPTC interrupt
+ */
+static void dptc_unmask_dptc_int(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVAIM, 0);
+}
+
+/*!
+ * Read the PTVAI bits from the CCM
+ *
+ * @return PTVAI bits value
+ */
+static unsigned long dptc_get_ptvai(void)
+{
+ return (mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_PTVAI_MASK)
+ >> MXC_CCM_PMCR0_PTVAI_OFFSET;
+}
+
+/*!
+ * Clear DCR bits of the CCM
+ */
+static void dptc_clear_dcr(void)
+{
+ if (!cpu_is_mxc91321()) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DCR, 0);
+ }
+}
+
+/*!
+ * If value equal to PTVAI bits indicates working point decrease
+ */
+#define DPTC_DECREASE (unsigned long)0x1
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase
+ */
+#define DPTC_INCREASE (unsigned long)0x2
+
+/*!
+ * If value equal to PTVAI bits indicates working point increase to maximum
+ */
+#define DPTC_EMERG (unsigned long)0x3
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+/*
+ * MCU will get DPTC interrupt
+ */
+static void dptc_set_ptvis(void)
+{
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_PTVIS,
+ MXC_CCM_PMCR0_PTVIS);
+}
+#endif
+#endif
+
+/*!
+ * 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)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state = rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE0,
+ MXC_CCM_PMCR0_DRCE0);
+ pr_debug("Ref circuit 0 enabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE1,
+ MXC_CCM_PMCR0_DRCE1);
+ pr_debug("Ref circuit 1 enabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE2,
+ MXC_CCM_PMCR0_DRCE2);
+ pr_debug("Ref circuit 2 enabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DRCE3,
+ MXC_CCM_PMCR0_DRCE3);
+ pr_debug("Ref circuit 3 enabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * 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)
+{
+ int ret_val;
+
+ if (params->dptc_is_active == FALSE) {
+ params->rc_state &= ~rc_state;
+
+ if (rc_state & 0x1) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE0, 0);
+ pr_debug("Ref circuit 0 disabled\n");
+ }
+ if (rc_state & 0x2) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE1, 0);
+ pr_debug("Ref circuit 1 disabled\n");
+ }
+ if (rc_state & 0x4) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE2, 0);
+ pr_debug("Ref circuit 2 disabled\n");
+ }
+ if (rc_state & 0x8) {
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0,
+ MXC_CCM_PMCR0_DRCE3, 0);
+ pr_debug("Ref circuit 3 disabled\n");
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+static void dptc_workqueue_handler(struct work_struct *work)
+{
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) work_data_bits(work);
+
+ pr_debug("In %s: PTVAI = %lu\n", __FUNCTION__, dptc_get_ptvai());
+ pr_debug("PMCR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ pr_debug("DCVR0 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR0));
+ pr_debug("DCVR1 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR1));
+ pr_debug("DCVR2 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR2));
+ pr_debug("DCVR3 = 0x%lx ", mxc_ccm_get_reg(MXC_CCM_DCVR3));
+ pr_debug("PTVAI = 0x%lx\n", ptvai);
+
+ if ((params->suspended == 0 && params->turbo_mode_active == 1)
+ || !(cpu_is_mxc91321())) {
+#ifndef CONFIG_MXC_DVFS_SDMA
+ switch (ptvai) {
+ /* Chip working point has decreased, lower working point by one */
+ case DPTC_DECREASE:
+ set_dptc_wp(params,
+ params->dvfs_dptc_tables_ptr->curr_wp + 1);
+ break;
+
+ /* Chip working point has increased, raise working point by one */
+ case DPTC_INCREASE:
+ set_dptc_wp(params,
+ params->dvfs_dptc_tables_ptr->curr_wp - 1);
+ break;
+
+ /*
+ * Chip working point has increased dramatically,
+ * raise working point to maximum */
+ case DPTC_EMERG:
+ set_dptc_wp(params,
+ params->dvfs_dptc_tables_ptr->curr_wp - 1);
+ break;
+
+ /* Unknown interrupt cause */
+ default:
+ break;
+ }
+#else
+ set_dptc_wp(params, params->dvfs_dptc_tables_ptr->curr_wp);
+ mxc_ccm_modify_reg(MXC_CCM_PMCR0, MXC_CCM_PMCR0_DPVV,
+ MXC_CCM_PMCR0_DPVV);
+#endif
+
+ /*
+ * If the DPTC module is still active, re-enable
+ * the DPTC hardware
+ */
+ if (params->dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+ }
+}
+
+/*!
+ * 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)
+{
+#ifndef CONFIG_MXC_DVFS_SDMA
+ ptvai = dptc_get_ptvai();
+
+ pr_debug("ptvai = 0x%lx (0x%x)!!!!!!!\n", ptvai,
+ __raw_readl(MXC_CCM_PMCR0));
+#ifdef CONFIG_ARCH_MXC91321
+ pr_debug("REF CIRCUIT: %lu\n", mxc_ccm_get_reg(MXC_CCM_DPTCDBG));
+#endif
+ if (ptvai != 0) {
+ dptc_mask_dptc_int();
+ dptc_disable_dptc();
+
+ schedule_delayed_work(&dptc_work, 0);
+ }
+#else
+ schedule_delayed_work(&dptc_work, 0);
+#endif
+}
+
+/*!
+ * This function updates the CPU voltage, produced by mc13783, by calling mc13783
+ * driver functions.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ * frequency.
+ *
+ */
+void set_pmic_voltage(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr, int wp)
+{
+ /* Call mc13783 functions */
+ t_regulator_voltage volt;
+
+ /* Normal mode setting */
+ if (cpu_is_mxc91321()) {
+ /* DVS mode setting */
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_switcher_set_dvs(SW_SW1A, volt);
+ } else {
+ /* Normal mode setting */
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+ }
+
+#ifdef CONFIG_MXC_DVFS
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[1];
+ pmic_power_switcher_set_dvs(SW_SW1A, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[2];
+ pmic_power_switcher_set_dvs(SW_SW1B, volt);
+
+ volt.sw1b = dvfs_dptc_tables_ptr->wp[wp].pmic_values[3];
+ pmic_power_switcher_set_stby(SW_SW1B, volt);
+#endif
+
+ if (cpu_is_mx31()) {
+ pr_debug("DPVV = 0x%lx (0x%lx)\n",
+ mxc_ccm_get_reg(MXC_CCM_PMCR0) & MXC_CCM_PMCR0_DPVV,
+ mxc_ccm_get_reg(MXC_CCM_PMCR0));
+ }
+}
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the mc13783, 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)
+{
+ int freq_index = 0;
+
+ /* Check if DPTC module isn't already active */
+ if (params->dptc_is_active == FALSE) {
+
+ enable_ref_circuits(params, params->rc_state);
+ disable_ref_circuits(params, ~params->rc_state);
+
+ if (cpu_is_mxc91321() && !params->turbo_mode_active) {
+ params->dptc_is_active = TRUE;
+ return 0;
+ }
+
+ /*
+ * Set the DPTC thresholds and mc13783 voltage to
+ * correspond to the current working point and frequency.
+ */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+
+ /* Mark DPCT module as active */
+ params->dptc_is_active = TRUE;
+
+ /* Enable the DPTC module and unmask the DPTC interrupt */
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+
+ return 0;
+ }
+
+ /* DPTC module already active return error */
+ return -EINVAL;
+}
+
+/*!
+ * 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)
+{
+ /* Check if DPTC module isn't already disabled */
+ if (params->dptc_is_active != FALSE) {
+
+ /* Disable the DPTC module and mask the DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ /* Set working point 0 */
+ set_dptc_wp(params, 0);
+
+ /* Mark DPCT module as inactive */
+ params->dptc_is_active = FALSE;
+
+ return 0;
+ }
+
+ /* DPTC module already disabled, return error */
+ return -EINVAL;
+}
+
+/*!
+ * 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)
+{
+ int freq_index = 0;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dvfs_get_dvsup();
+#endif
+
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((new_wp < params->dvfs_dptc_tables_ptr->wp_num)
+ && (new_wp >= 0)) {
+ /* Set current working point index to new index */
+ params->dvfs_dptc_tables_ptr->curr_wp = new_wp;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (new_wp >= params->dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ params->dvfs_dptc_tables_ptr->curr_wp =
+ params->dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ /* Check if new index is smaller than 0. */
+ if (new_wp < 0) {
+ /* Set current working point index to 0 (minimal value) */
+ params->dvfs_dptc_tables_ptr->curr_wp = 0;
+ }
+#ifndef CONFIG_MXC_DVFS_SDMA
+ /* Update the DPTC hardware thresholds */
+ update_dptc_thresholds(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ freq_index);
+#else
+ params->prev_wp = params->dvfs_dptc_tables_ptr->curr_wp;
+#endif
+
+ /* Update the mc13783 voltage */
+ set_pmic_voltage(params->dvfs_dptc_tables_ptr,
+ params->dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Write DPTC changes in the DPTC log buffer */
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp, freq_index);
+
+ pr_debug("Current wp: %d\n", params->dvfs_dptc_tables_ptr->curr_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 * dvfs_dptc_tables_ptr,
+ int wp, int freq_index)
+{
+ dcvr_state *dcvr;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+
+ dcvr_state **dcvr_arr;
+ dcvr_state *curr_freq_dcvr;
+
+ dcvr_arr = dvfs_dptc_tables_ptr->dcvr;
+ dcvr_arr = sdma_phys_to_virt((unsigned long)dcvr_arr);
+ curr_freq_dcvr = dcvr_arr[freq_index];
+ curr_freq_dcvr = sdma_phys_to_virt((unsigned long)curr_freq_dcvr);
+ dcvr = &curr_freq_dcvr[wp];
+#else
+ /* Calculate current table entry offset in the DPTC translation table */
+ dcvr = &dvfs_dptc_tables_ptr->dcvr[freq_index][wp];
+#endif
+ /* Update DPTC threshold registers */
+ mxc_ccm_modify_reg(MXC_CCM_DCVR0, 0xffffffff, dcvr->dcvr_reg[0].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR1, 0xffffffff, dcvr->dcvr_reg[1].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR2, 0xffffffff, dcvr->dcvr_reg[2].AsInt);
+ mxc_ccm_modify_reg(MXC_CCM_DCVR3, 0xffffffff, dcvr->dcvr_reg[3].AsInt);
+}
+
+/*!
+ * This function increments a log buffer index (head or tail)
+ * by the value of val.
+ *
+ * @param index pointer to the DPTC log buffer index that
+ * we wish to change.
+ * @param val the value in which the index should be incremented.
+ *
+ */
+static void inc_log_index(int *index, int val)
+{
+ *index = (*index + val) % LOG_ENTRIES;
+}
+
+/*!
+ * This function returns the number of entries in the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static int get_entry_count(dptc_log_s * dptc_log)
+{
+ return ((dptc_log->head - dptc_log->tail + LOG_ENTRIES) % LOG_ENTRIES);
+}
+
+/*!
+ * This function is used by the proc file system to read the DPTC log buffer.
+ * Each time the DPTC proc file system file is read this function is called
+ * and returns the data written in the log buffer.
+ *
+ * @param buf pointer to the buffer the data should 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 current offset in the DPTC proc file.
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. should be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ *
+ */
+static int read_log(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int entries_to_read;
+ int num_of_entries;
+ int entries_to_end_of_buffer, entries_left;
+ void *entry_ptr;
+ char *buf_ptr;
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) data;
+
+ /* Calculate number of log entries to read */
+ entries_to_read = count / sizeof(dptc_log_entry_s);
+ /* Get the number of current log buffer entries */
+ num_of_entries = get_entry_count(&params->dptc_log_buffer);
+
+ /*
+ * If number of entries to read is larger that the number of entries
+ * in the log buffer set number of entries to read to number of
+ * entries in the log buffer and set eof flag to 1
+ */
+ if (num_of_entries < entries_to_read) {
+ entries_to_read = num_of_entries;
+ *eof = 1;
+ }
+
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&params->dptc_log_buffer.mutex)) {
+ return -EAGAIN;
+ }
+
+ if (num_of_entries == 0 && offset == 0) {
+ inc_log_index(&params->dptc_log_buffer.tail, -1);
+ num_of_entries++;
+ entries_to_read++;
+ }
+
+ /* get the pointer of the last (oldest) entry in the log buffer */
+ entry_ptr = (void *)&params->dptc_log_buffer.
+ entries[params->dptc_log_buffer.tail];
+
+ /* Check if tail index wraps during current read */
+ if ((params->dptc_log_buffer.tail + entries_to_read) < LOG_ENTRIES) {
+ /* No tail wrap around copy data from log buffer to buf */
+ memcpy(buf, entry_ptr,
+ (entries_to_read * sizeof(dptc_log_entry_s)));
+ } else {
+ /*
+ * Tail wrap around.
+ * First copy data from current position until end of buffer,
+ * after that copy the rest from start of the log buffer.
+ */
+ entries_to_end_of_buffer = LOG_ENTRIES -
+ params->dptc_log_buffer.tail;
+ memcpy(buf, entry_ptr,
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s)));
+
+ entry_ptr = (void *)&params->dptc_log_buffer.entries[0];
+ buf_ptr = buf +
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s));
+ entries_left = entries_to_read - entries_to_end_of_buffer;
+ memcpy(buf_ptr, entry_ptr,
+ (entries_left * sizeof(dptc_log_entry_s)));
+ }
+
+ /* Increment the tail index by the number of entries read */
+ inc_log_index(&params->dptc_log_buffer.tail, entries_to_read);
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&params->dptc_log_buffer.mutex);
+
+ /* set start of data to point to buf */
+ *start = buf;
+
+ return (entries_to_read * sizeof(dptc_log_entry_s));
+}
+
+/*!
+ * This function initializes the DPTC log buffer.
+ *
+ * @param params pointer to \b dvfs_dptc_params_s.
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ */
+void init_dptc_log(dvfs_dptc_params_s * params, dptc_log_s * dptc_log)
+{
+ dptc_log->tail = 0;
+ dptc_log->head = 0;
+
+ /* initialize log buffer mutex used for accessing the log buffer */
+ sema_init(&dptc_log->mutex, 1);
+
+ /* add the first log buffer entry */
+ add_dptc_log_entry(params, dptc_log,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_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)
+{
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&dptc_log->mutex)) {
+ return;
+ }
+
+ /* Write values to log buffer */
+ dptc_log->entries[dptc_log->head].jiffies = jiffies;
+ dptc_log->entries[dptc_log->head].wp = wp;
+
+ dptc_log->entries[dptc_log->head].voltage = wp;
+
+#ifdef CONFIG_MXC_DVFS
+ freq_index = dptc_get_ptvai();
+#endif
+
+ dptc_log->entries[dptc_log->head].freq = freq_index;
+
+ /* Increment the head index by 1 */
+ inc_log_index(&dptc_log->head, 1);
+ /* If head index reaches the tail increment the tail by 1 */
+ if (dptc_log->head == dptc_log->tail) {
+ inc_log_index(&dptc_log->tail, 1);
+ }
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&dptc_log->mutex);
+}
+
+/*!
+ * This function is called to put the DPTC in a low power state.
+ *
+ */
+void dptc_suspend(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ if (cpu_is_mxc91321() && !dvfs_dptc_params->turbo_mode_active) {
+ return;
+ }
+ dptc_disable_dptc();
+ set_dptc_wp(dvfs_dptc_params,
+ dvfs_dptc_params->
+ dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+#endif
+}
+
+/*!
+ * This function is called to resume the DPTC from a low power state.
+ *
+ */
+void dptc_resume(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params->dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+#endif
+}
+
+/*!
+ * 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)
+{
+ if (cpu_is_mxc91321()) {
+ if (status == 1) {
+ dvfs_dptc_params->turbo_mode_active = 1;
+ set_dptc_wp(dvfs_dptc_params,
+ dvfs_dptc_params->dvfs_dptc_tables_ptr->
+ curr_wp);
+ dvfs_dptc_params->suspended = 0;
+ } else {
+ dvfs_dptc_params->turbo_mode_active = 0;
+ if (cpu_is_mxc91321()) {
+ dvfs_dptc_params->suspended =
+ dvfs_dptc_params->turbo_mode_active;
+ return;
+ } else {
+ dvfs_dptc_params->suspended = 1;
+ }
+ set_pmic_voltage(dvfs_dptc_params->dvfs_dptc_tables_ptr,
+ dvfs_dptc_params->
+ dvfs_dptc_tables_ptr->wp_num - 1);
+ }
+ }
+}
+
+/*!
+ * This function initializes the DPTC hardware
+ *
+ * @param params pointer to the DPTC driver parameters structure.
+ *
+ */
+int __init init_dptc_controller(dvfs_dptc_params_s * params)
+{
+ dvfs_dptc_params = params;
+
+ INIT_DELAYED_WORK(&dptc_work, dptc_workqueue_handler);
+
+ if (create_proc_read_entry(PROC_NODE_NAME, 0,
+ NULL, read_log, params) == NULL) {
+ /*
+ * Error creating proc file system entry.
+ * Exit and return error code
+ */
+ printk(KERN_ERR "DPTC: Unable create proc entry");
+ return -EFAULT;
+ }
+
+ /* Initialize the DPTC log buffer */
+ init_dptc_log(params, &params->dptc_log_buffer);
+
+ set_dptc_curr_freq(params, 0);
+
+ if (cpu_is_mxc91321()) {
+ dptc_set_turbo_mode(0);
+ } else {
+ set_dptc_wp(params, 0);
+ }
+
+ /* By default all reference circuits are enabled */
+ params->rc_state = DPTC_REF_CIRCUITS_STATUS;
+
+ if (!cpu_is_mxc91321())
+ dptc_clear_dcr();
+
+ /* Disable DPTC hardware and mask DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DVFS_SDMA
+ dptc_set_ptvis();
+#endif
+#endif
+ printk(KERN_INFO "DPTC controller initialized\n");
+
+ return 0;
+}
+
+/*!
+ * 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)
+{
+ /*
+ * Check if the new index value is a valid frequency index (smaller
+ * than the maximal index in the DPTC table) and if the DPTC module
+ * is disabled.
+ */
+ if ((freq_index < params->dvfs_dptc_tables_ptr->dvfs_state_num)
+ && (params->dptc_is_active == FALSE)) {
+ /*
+ * Index is valid and DPTC module is
+ * disabled -> change frequency index.
+ */
+ params->current_freq_index = freq_index;
+ add_dptc_log_entry(params, &params->dptc_log_buffer,
+ params->dvfs_dptc_tables_ptr->curr_wp,
+ params->current_freq_index);
+
+ return 0;
+ }
+
+ /* Invalid index or DPTC module is active -> return error */
+ return -EINVAL;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * DPTC SDMA callback.
+ * Updates the mc13783 voltage
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+void dptc_sdma_callback(dvfs_dptc_params_s * params)
+{
+ printk(KERN_INFO "In %s: params->dvfs_dptc_tables_ptr->curr_wp = %d\n",
+ __FUNCTION__, params->dvfs_dptc_tables_ptr->curr_wp);
+}
+#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)
+{
+ dptc_suspend();
+ return 0;
+}
+
+/*!
+ * 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 *pdev)
+{
+ dptc_resume();
+ return 0;
+}
+
+EXPORT_SYMBOL(dptc_suspend);
+EXPORT_SYMBOL(dptc_resume);
diff --git a/drivers/mxc/pm/dptc_mx27.c b/drivers/mxc/pm/dptc_mx27.c
new file mode 100644
index 000000000000..9e60294d63dd
--- /dev/null
+++ b/drivers/mxc/pm/dptc_mx27.c
@@ -0,0 +1,1416 @@
+/*
+ * 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 dptc_mx27.c
+ *
+ * @brief Driver for the Freescale Semiconductor MX27 DPTC module.
+ *
+ * The DPTC driver is designed as a character driver which interacts with the
+ * MX27 DPTC 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 voltage, increase voltage or
+ * emergency) and changes the CPU voltage according to translation table that
+ * is loaded into the driver(the voltage changes are done by calling some
+ * routines in the mc13783 driver). The driver read method is used to read the
+ * currently loaded DPTC translation table and the write method is used
+ * in-order to update the translation table. Driver ioctls are used to change
+ * driver parameters and enable/disable the DPTC operation.
+ *
+ * @ingroup PM_MX27
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/arch/pmic_power.h>
+#include <asm/arch/dvfs_dptc_struct.h>
+#include "dvfs_dptc.h"
+
+#define MX27_PMCR_BASE_ADDR (SYSCTRL_BASE_ADDR + 0x60)
+#define MX27_DCVR_BASE_ADDR (SYSCTRL_BASE_ADDR + 0x64)
+#define MX27_DCVR0_OFFSET 0
+#define MX27_DCVR1_OFFSET 4
+#define MX27_DCVR2_OFFSET 8
+#define MX27_DCVR3_OFFSET 12
+
+#define MX27_PMCR_MC_STATUS_OFFSET 31
+#define MX27_PMCR_MC_STATUS_MASK (1 << 31)
+#define MX27_PMCR_EM_INTR_OFFSET 30
+#define MX27_PMCR_EM_INTR_MASK (1 << 30)
+#define MX27_PMCR_UP_INTR_OFFSET 29
+#define MX27_PMCR_UP_INTR_MASK (1 << 29)
+#define MX27_PMCR_LO_INTR_OFFSET 28
+#define MX27_PMCR_LO_INTR_MASK (1 << 28)
+#define MX27_PMCR_REFCNT_OFFSET 16
+#define MX27_PMCR_REFCNT_MASK (0x7FF << 16)
+#define MX27_PMCR_DCR_OFFSET 9
+#define MX27_PMCR_DCR_MASK (1 << 9)
+#define MX27_PMCR_RCLKON_OFFSET 8
+#define MX27_PMCR_RCLKON_MASK (1 << 8)
+#define MX27_PMCR_DRCE3_OFFSET 7
+#define MX27_PMCR_DRCE3_MASK (1 << 7)
+#define MX27_PMCR_DRCE2_OFFSET 6
+#define MX27_PMCR_DRCE2_MASK (1 << 6)
+#define MX27_PMCR_DRCE1_OFFSET 5
+#define MX27_PMCR_DRCE1_MASK (1 << 5)
+#define MX27_PMCR_DRCE0_OFFSET 4
+#define MX27_PMCR_DRCE0_MASK (1 << 4)
+#define MX27_PMCR_DIM_OFFSET 2
+#define MX27_PMCR_DIM_MASK (0x3 << 2)
+#define MX27_PMCR_DIE_OFFSET 1
+#define MX27_PMCR_DIE_MASK (1 << 1)
+#define MX27_PMCR_DPTEN_OFFSET 0
+#define MX27_PMCR_DPTEN_MASK (1 << 0)
+
+/*!
+ * DPTC proc file system entry name
+ */
+#define PROC_NODE_NAME "dptc"
+
+/*
+ * Prototypes
+ */
+static int dptc_mx27_open(struct inode *inode, struct file *filp);
+static int dptc_mx27_release(struct inode *inode, struct file *filp);
+static int dptc_mx27_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+static irqreturn_t dptc_mx27_irq(int irq, void *dev_id);
+
+/*
+ * Global variables
+ */
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s dptc_params;
+
+static struct work_struct dptc_work;
+static volatile u32 dptc_intr_status;
+static u32 dptc_initial_wp;
+
+/*!
+ * Holds the automatically selected DPTC driver major number.
+ */
+static int major;
+
+static struct class *mxc_dvfs_dptc_class;
+
+/*
+ * This mutex makes the Read,Write and IOCTL command mutual exclusive.
+ */
+DECLARE_MUTEX(access_mutex);
+
+/*!
+ * This structure contains pointers for device driver entry point.
+ * The driver register function in init module will call this
+ * structure.
+ */
+static struct file_operations fops = {
+ .open = dptc_mx27_open,
+ .release = dptc_mx27_release,
+ .ioctl = dptc_mx27_ioctl,
+};
+
+static
+void mx27_pmcr_modify_reg(u32 mask, u32 val)
+{
+ volatile u32 reg;
+ reg = __raw_readl(IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+ reg = (reg & (~mask)) | val;
+ __raw_writel(reg, IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+}
+
+static
+void mx27_dcvr_modify_reg(u32 offset, u32 val)
+{
+ __raw_writel(val, IO_ADDRESS(MX27_DCVR_BASE_ADDR) + offset);
+}
+
+/*!
+ * Enable DPTC hardware
+ */
+static void dptc_enable_dptc(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DPTEN_MASK, 1);
+}
+
+/*!
+ * Disable DPTC hardware
+ */
+static void dptc_disable_dptc(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DPTEN_MASK, 0);
+}
+
+/*!
+ * Mask DPTC interrupt
+ */
+static void dptc_mask_dptc_int(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DIE_MASK, (0 << MX27_PMCR_DIE_OFFSET));
+}
+
+/*!
+ * Unmask DPTC interrupt
+ */
+static void dptc_unmask_dptc_int(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DIE_MASK, (1 << MX27_PMCR_DIE_OFFSET));
+}
+
+/*!
+ * Clear DCR bits of the CCM
+ */
+static void dptc_clear_dcr(void)
+{
+ mx27_pmcr_modify_reg(MX27_PMCR_DCR_MASK, (0 << MX27_PMCR_DCR_OFFSET));
+}
+
+/*!
+ * This function enables the DPTC reference circuits.
+ *
+ * @param rc_state each high bit specifies which
+ * reference circuite to enable
+ * @return 0 on success, error code on failure
+ */
+static int enable_ref_circuits(unsigned char rc_state)
+{
+ int ret_val;
+
+ if (dptc_params.dptc_is_active == FALSE) {
+ dptc_params.rc_state = rc_state;
+
+ if (rc_state & 0x1) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE0_MASK,
+ (1 << MX27_PMCR_DRCE0_OFFSET));
+ }
+ if (rc_state & 0x2) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE1_MASK,
+ (1 << MX27_PMCR_DRCE1_OFFSET));
+ }
+ if (rc_state & 0x4) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE2_MASK,
+ (1 << MX27_PMCR_DRCE2_OFFSET));
+ }
+ if (rc_state & 0x8) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE3_MASK,
+ (1 << MX27_PMCR_DRCE3_OFFSET));
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function disables the DPTC reference circuits.
+ *
+ * @param rc_state each high bit specifies which
+ * reference circuite to disable
+ * @return 0 on success, error code on failure
+ */
+static int disable_ref_circuits(unsigned char rc_state)
+{
+ int ret_val;
+
+ if (dptc_params.dptc_is_active == FALSE) {
+ dptc_params.rc_state &= ~rc_state;
+
+ if (rc_state & 0x1) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE0_MASK,
+ (0 << MX27_PMCR_DRCE0_OFFSET));
+ }
+ if (rc_state & 0x2) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE1_MASK,
+ (0 << MX27_PMCR_DRCE1_OFFSET));
+ }
+ if (rc_state & 0x4) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE2_MASK,
+ (0 << MX27_PMCR_DRCE2_OFFSET));
+ }
+ if (rc_state & 0x8) {
+ mx27_pmcr_modify_reg(MX27_PMCR_DRCE3_MASK,
+ (0 << MX27_PMCR_DRCE3_OFFSET));
+ }
+
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function updates the CPU voltage, produced by MC13783, by calling
+ * MC13783 driver functions.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ *
+ */
+static void set_pmic_voltage(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr, int wp)
+{
+ /* Call MC13783 functions */
+ t_regulator_voltage volt;
+
+ volt.sw1a = dvfs_dptc_tables_ptr->wp[wp].pmic_values[0];
+ pmic_power_regulator_set_voltage(SW_SW1A, volt);
+}
+
+/*!
+ * This function updates the DPTC threshold registers.
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DPTC translation table.
+ * @param wp current wp value.
+ *
+ */
+static void update_dptc_thresholds(dvfs_dptc_tables_s * dvfs_dptc_tables_ptr,
+ int wp)
+{
+ dcvr_state *dcvr;
+
+ /* Calculate current table entry offset in the DPTC translation table */
+ dcvr = &dvfs_dptc_tables_ptr->dcvr[0][wp];
+
+ /* Update DPTC threshold registers */
+ mx27_dcvr_modify_reg(MX27_DCVR0_OFFSET, dcvr->dcvr_reg[0].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR1_OFFSET, dcvr->dcvr_reg[1].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR2_OFFSET, dcvr->dcvr_reg[2].AsInt);
+ mx27_dcvr_modify_reg(MX27_DCVR3_OFFSET, dcvr->dcvr_reg[3].AsInt);
+}
+
+/*!
+ * This function increments a log buffer index (head or tail)
+ * by the value of val.
+ *
+ * @param index pointer to the DPTC log buffer index that
+ * we wish to change.
+ * @param val the value in which the index should be incremented.
+ *
+ */
+static void inc_log_index(int *index, int val)
+{
+ *index = (*index + val) % LOG_ENTRIES;
+}
+
+/*!
+ * This function adds a new entry to the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ * @param wp value of the working point index written
+ * to the log buffer.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static void add_dptc_log_entry(dptc_log_s * dptc_log, int wp)
+{
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&dptc_log->mutex)) {
+ return;
+ }
+
+ /* Write values to log buffer */
+ dptc_log->entries[dptc_log->head].jiffies = jiffies;
+ dptc_log->entries[dptc_log->head].wp = wp;
+
+ dptc_log->entries[dptc_log->head].voltage = wp;
+ dptc_log->entries[dptc_log->head].freq = 0;
+
+ /* Increment the head index by 1 */
+ inc_log_index(&dptc_log->head, 1);
+ /* If head index reaches the tail increment the tail by 1 */
+ if (dptc_log->head == dptc_log->tail) {
+ inc_log_index(&dptc_log->tail, 1);
+ }
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&dptc_log->mutex);
+}
+
+/*!
+ * 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 new_wp New working point index value to be set.
+ *
+ */
+static void set_dptc_wp(int new_wp)
+{
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((new_wp < dptc_params.dvfs_dptc_tables_ptr->wp_num)
+ && (new_wp >= 0)) {
+ /* Set current working point index to new index */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = new_wp;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (new_wp >= dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp =
+ dptc_params.dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ /* Check if new index is smaller than 0. */
+ if (new_wp < 0) {
+ /* Set current working point index to 0 (minimal value) */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = 0;
+ }
+
+ /* Update the DPTC hardware thresholds */
+ update_dptc_thresholds(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Update the MC13783 voltage */
+ set_pmic_voltage(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ /* Write DPTC changes in the DPTC log buffer */
+ add_dptc_log_entry(&dptc_params.dptc_log_buffer,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+}
+
+static void dptc_workqueue_handler(struct work_struct *work)
+{
+ if (dptc_intr_status & 0x4) {
+ /* Chip working point has increased dramatically,
+ * raise working point to maximum */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 2);
+ } else if (dptc_intr_status & 0x2) {
+ /* Chip working point has increased, raise working point
+ * by one */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp + 1);
+ } else {
+ /* Chip working point has decreased, lower working point
+ * by one */
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+
+ /*
+ * If the DPTC module is still active, re-enable
+ * the DPTC hardware
+ */
+ if (dptc_params.dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+}
+
+/*!
+ * This function enables the DPTC module. this function updates the DPTC
+ * thresholds, updates the MC13783, unmasks the DPTC interrupt and enables
+ * the DPTC module
+ *
+ * @return 0 if DPTC module was enabled else returns -EINVAL.
+ */
+static int start_dptc(void)
+{
+ /* Check if DPTC module isn't already active */
+ if (dptc_params.dptc_is_active == FALSE) {
+
+ enable_ref_circuits(dptc_params.rc_state);
+ disable_ref_circuits(~dptc_params.rc_state);
+
+ /*
+ * Set the DPTC thresholds and MC13783 voltage to
+ * correspond to the current working point and frequency.
+ */
+ set_pmic_voltage(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+
+ update_dptc_thresholds(dptc_params.dvfs_dptc_tables_ptr,
+ dptc_params.dvfs_dptc_tables_ptr->
+ curr_wp);
+
+ /* Mark DPTC module as active */
+ dptc_params.dptc_is_active = TRUE;
+
+ /* Enable the DPTC module and unmask the DPTC interrupt */
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+
+ return 0;
+ }
+
+ /* DPTC module already active return error */
+ return -EINVAL;
+}
+
+/*!
+ * This function disables the DPTC module.
+ *
+ * @return 0 if DPTC module was disabled else returns -EINVAL.
+ */
+static int stop_dptc(void)
+{
+ /* Check if DPTC module isn't already disabled */
+ if (dptc_params.dptc_is_active != FALSE) {
+
+ /* Disable the DPTC module and mask the DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ /* Set working point to default */
+ set_dptc_wp(dptc_initial_wp);
+
+ /* Mark DPCT module as inactive */
+ dptc_params.dptc_is_active = FALSE;
+
+ return 0;
+ }
+
+ /* DPTC module already disabled, return error */
+ return -EINVAL;
+}
+
+static void init_dptc_wp(void)
+{
+ /* Call MC13783 functions */
+ t_regulator_voltage volt;
+ int i;
+
+ /* Normal mode setting */
+ pmic_power_regulator_get_voltage(SW_SW1A, &volt);
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ if (volt.sw1a ==
+ dptc_params.dvfs_dptc_tables_ptr->wp[i].pmic_values[0]) {
+ break;
+ }
+ }
+
+ /*
+ * Check if new index is smaller than the maximal working point
+ * index in the DPTC translation table and larger that 0.
+ */
+ if ((i < dptc_params.dvfs_dptc_tables_ptr->wp_num) && (i >= 0)) {
+ /* Set current working point index to new index */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp = i;
+ }
+
+ /*
+ * Check if new index is larger than the maximal working point index in
+ * the DPTC translation table.
+ */
+ if (i >= dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ /*
+ * Set current working point index to maximal working point
+ * index in the DPTC translation table.
+ */
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp =
+ dptc_params.dvfs_dptc_tables_ptr->wp_num - 1;
+ }
+
+ dptc_initial_wp = dptc_params.dvfs_dptc_tables_ptr->curr_wp;
+
+ /* Initialize the log buffer */
+ add_dptc_log_entry(&dptc_params.dptc_log_buffer,
+ dptc_params.dvfs_dptc_tables_ptr->curr_wp);
+}
+
+/*!
+ * This function returns the number of entries in the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ * @return number of log buffer entries.
+ *
+ */
+static int get_entry_count(dptc_log_s * dptc_log)
+{
+ return ((dptc_log->head - dptc_log->tail + LOG_ENTRIES) % LOG_ENTRIES);
+}
+
+/*!
+ * This function is used by the proc file system to read the DPTC log buffer.
+ * Each time the DPTC proc file system file is read this function is called
+ * and returns the data written in the log buffer.
+ *
+ * @param buf pointer to the buffer the data should 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 current offset in the DPTC proc file.
+ * @param count number of bytes to read.
+ * @param eof pointer to eof flag. should be set to 1 when
+ * reaching eof.
+ * @param data driver specific data pointer.
+ *
+ * @return number byte read from the log buffer.
+ *
+ */
+static int read_log(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int entries_to_read;
+ int num_of_entries;
+ int entries_to_end_of_buffer, entries_left;
+ void *entry_ptr;
+ char *buf_ptr;
+ dvfs_dptc_params_s *params;
+
+ params = (dvfs_dptc_params_s *) data;
+
+ /* Calculate number of log entries to read */
+ entries_to_read = count / sizeof(dptc_log_entry_s);
+ /* Get the number of current log buffer entries */
+ num_of_entries = get_entry_count(&params->dptc_log_buffer);
+
+ /*
+ * If number of entries to read is larger that the number of entries
+ * in the log buffer set number of entries to read to number of
+ * entries in the log buffer and set eof flag to 1
+ */
+ if (num_of_entries < entries_to_read) {
+ entries_to_read = num_of_entries;
+ *eof = 1;
+ }
+
+ /*
+ * Down the log buffer mutex to exclude others from reading and
+ * writing to the log buffer.
+ */
+ if (down_interruptible(&params->dptc_log_buffer.mutex)) {
+ return -EAGAIN;
+ }
+
+ if (num_of_entries == 0 && offset == 0) {
+ inc_log_index(&params->dptc_log_buffer.tail, -1);
+ num_of_entries++;
+ entries_to_read++;
+ }
+
+ /* get the pointer of the last (oldest) entry in the log buffer */
+ entry_ptr = (void *)&params->dptc_log_buffer.
+ entries[params->dptc_log_buffer.tail];
+
+ /* Check if tail index wraps during current read */
+ if ((params->dptc_log_buffer.tail + entries_to_read) < LOG_ENTRIES) {
+ /* No tail wrap around copy data from log buffer to buf */
+ memcpy(buf, entry_ptr,
+ (entries_to_read * sizeof(dptc_log_entry_s)));
+ } else {
+ /*
+ * Tail wrap around.
+ * First copy data from current position until end of buffer,
+ * after that copy the rest from start of the log buffer.
+ */
+ entries_to_end_of_buffer = LOG_ENTRIES -
+ params->dptc_log_buffer.tail;
+ memcpy(buf, entry_ptr,
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s)));
+
+ entry_ptr = (void *)&params->dptc_log_buffer.entries[0];
+ buf_ptr = buf +
+ (entries_to_end_of_buffer * sizeof(dptc_log_entry_s));
+ entries_left = entries_to_read - entries_to_end_of_buffer;
+ memcpy(buf_ptr, entry_ptr,
+ (entries_left * sizeof(dptc_log_entry_s)));
+ }
+
+ /* Increment the tail index by the number of entries read */
+ inc_log_index(&params->dptc_log_buffer.tail, entries_to_read);
+
+ /* Up the log buffer mutex to allow access to the log buffer */
+ up(&params->dptc_log_buffer.mutex);
+
+ /* set start of data to point to buf */
+ *start = buf;
+
+ return (entries_to_read * sizeof(dptc_log_entry_s));
+}
+
+/*!
+ * This function initializes the DPTC log buffer.
+ *
+ * @param dptc_log pointer to the DPTC log buffer structure.
+ *
+ */
+static void init_dptc_log(dptc_log_s * dptc_log)
+{
+ dptc_log->tail = 0;
+ dptc_log->head = 0;
+
+ /* initialize log buffer mutex used for accessing the log buffer */
+ sema_init(&dptc_log->mutex, 1);
+}
+
+/*!
+ * This function initializes the DPTC hardware
+ *
+ */
+static int init_dptc_controller(void)
+{
+ INIT_WORK(&dptc_work, dptc_workqueue_handler);
+
+ if (create_proc_read_entry(PROC_NODE_NAME, 0,
+ NULL, read_log, &dptc_params) == NULL) {
+ /*
+ * Error creating proc file system entry.
+ * Exit and return error code
+ */
+ printk(KERN_ERR "DPTC: Unable create proc entry");
+ return -EFAULT;
+ }
+
+ /* Initialize the DPTC log buffer */
+ init_dptc_log(&dptc_params.dptc_log_buffer);
+
+ init_dptc_wp();
+
+ /* By default all reference circuits are enabled */
+ dptc_params.rc_state = DPTC_REF_CIRCUITS_STATUS;
+
+ dptc_clear_dcr();
+
+ /* Disable DPTC hardware and mask DPTC interrupt */
+ dptc_disable_dptc();
+ dptc_mask_dptc_int();
+
+ printk(KERN_INFO "DPTC controller initialized\n");
+ return 0;
+}
+
+/*!
+ * 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)
+{
+ if (dptc_params.dptc_is_active) {
+ dptc_disable_dptc();
+ set_dptc_wp(dptc_params.dvfs_dptc_tables_ptr->curr_wp - 1);
+ }
+
+ dptc_params.suspended = 1;
+
+ return 0;
+}
+
+/*!
+ * 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 *pdev)
+{
+ dptc_params.suspended = 0;
+
+ if (dptc_params.dptc_is_active) {
+ dptc_enable_dptc();
+ dptc_unmask_dptc_int();
+ }
+
+ return 0;
+}
+
+/*!
+ * This function frees power management table structures
+ */
+static void free_dvfs_dptc_table(void)
+{
+ int i;
+
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ kfree(dptc_params.dvfs_dptc_tables_ptr->dcvr[i]);
+ }
+
+ kfree(dptc_params.dvfs_dptc_tables_ptr->dcvr);
+ kfree(dptc_params.dvfs_dptc_tables_ptr->table);
+ kfree(dptc_params.dvfs_dptc_tables_ptr->wp);
+
+ kfree(dptc_params.dvfs_dptc_tables_ptr);
+
+ dptc_params.dvfs_dptc_tables_ptr = 0;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * reads the next line of the table in text format
+ *
+ * @param str pointer to the previous line
+ *
+ * @return pointer to the next line
+ */
+static char *pm_table_get_next_line(char *str)
+{
+ char *line_ptr;
+ int flag = 0;
+
+ if (strlen(str) == 0)
+ return str;
+
+ line_ptr = strchr(str, '\n') + 1;
+
+ while (!flag) {
+ if (strlen(line_ptr) == 0) {
+ flag = 1;
+ } else if (line_ptr[0] == '\n') {
+ line_ptr++;
+ } else if (line_ptr[0] == '#') {
+ line_ptr = pm_table_get_next_line(line_ptr);
+ } else {
+ flag = 1;
+ }
+ }
+
+ return line_ptr;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * sets the values of DVFS & DPTC tables from
+ * table in text format
+ *
+ * @param pm_table pointer to the table in binary format
+ * @param pm_str pointer to the table in text format
+ *
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_parse_table(dvfs_dptc_tables_s * pm_table, char *pm_str)
+{
+ char *pm_str_ptr;
+ int i, j, n;
+ dptc_wp *wp;
+
+ pm_str_ptr = pm_str;
+
+ n = sscanf(pm_str_ptr, "WORKING POINT %d\n", &pm_table->wp_num);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WORKING POINT number\n");
+ return -1;
+ }
+
+ pm_table->curr_wp = 0;
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ pm_table->dvfs_state_num = 1;
+
+ pm_table->wp =
+ (dptc_wp *) kmalloc(sizeof(dptc_wp) * pm_table->wp_num, GFP_KERNEL);
+ if (!pm_table->wp) {
+ printk(KERN_ERR "Failed allocating memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->wp_num; i++) {
+
+ wp = &pm_table->wp[i];
+
+ wp->wp_index = i;
+
+ n = sscanf(pm_str_ptr, "WP 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0]);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ kfree(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ }
+
+ pm_table->table =
+ (dvfs_state *) kmalloc(sizeof(dvfs_state) *
+ pm_table->dvfs_state_num, GFP_KERNEL);
+
+ if (!pm_table->table) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ kfree(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ pm_table->dcvr =
+ (dcvr_state **) kmalloc(sizeof(dcvr_state *) *
+ pm_table->dvfs_state_num, GFP_KERNEL);
+
+ if (!pm_table->dcvr) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ kfree(pm_table->table);
+ kfree(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ pm_table->dcvr[i] =
+ (dcvr_state *) kmalloc(sizeof(dcvr_state) *
+ pm_table->wp_num, GFP_KERNEL);
+
+ if (!pm_table->dcvr[i]) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+
+ for (j = i - 1; j >= 0; j--) {
+ kfree(pm_table->dcvr[j]);
+ }
+
+ kfree(pm_table->dcvr);
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < pm_table->wp_num; j++) {
+
+ n = sscanf(pm_str_ptr, "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ &pm_table->dcvr[i][j].dcvr_reg[0].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[1].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[2].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[3].AsInt);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+
+ for (j = i; j >= 0; j--) {
+ kfree(pm_table->dcvr[j]);
+ }
+ kfree(pm_table->dcvr);
+ kfree(pm_table->table);
+ kfree(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initializes the default values of DVFS & DPTC table
+ *
+ * @return 0 on success, error code on failure
+ */
+static int __init dvfs_dptc_init_default_table(void)
+{
+ int res = 0;
+ char *table_str;
+
+ dvfs_dptc_tables_s *default_table;
+
+ default_table = kmalloc(sizeof(dvfs_dptc_tables_s), GFP_KERNEL);
+
+ if (!default_table) {
+ return -ENOMEM;
+ }
+
+ table_str = default_table_str;
+
+ memset(default_table, 0, sizeof(dvfs_dptc_tables_s));
+ res = dvfs_dptc_parse_table(default_table, table_str);
+
+ if (res == 0) {
+ dptc_params.dvfs_dptc_tables_ptr = default_table;
+ }
+
+ return res;
+}
+
+/*!
+ * This function is called when the driver is opened. This function
+ * checks if the user that open the device has root privileges.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dptc_mx27_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * check if the program that opened the driver has root
+ * privileges, if not return error.
+ */
+ if (!capable(CAP_SYS_ADMIN)) {
+ return -EACCES;
+ }
+
+ if (dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called when the driver is close.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ *
+ */
+static int dptc_mx27_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * This function dumps dptc translation table into string pointer
+ *
+ * @param str string pointer
+ */
+static void dvfs_dptc_dump_table(char *str)
+{
+ int i, j;
+ dcvr_state **dcvr_arr;
+ dcvr_state *dcvr_row;
+
+ memset(str, 0, MAX_TABLE_SIZE);
+
+ sprintf(str, "WORKING POINT %d\n",
+ dptc_params.dvfs_dptc_tables_ptr->wp_num);
+ str += strlen(str);
+
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ sprintf(str, "WP 0x%x\n", (unsigned int)
+ dptc_params.dvfs_dptc_tables_ptr->wp[i].pmic_values[0]);
+ str += strlen(str);
+ }
+
+ dcvr_arr = dptc_params.dvfs_dptc_tables_ptr->dcvr;
+ for (i = 0; i < dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ dcvr_row = dcvr_arr[i];
+
+ for (j = 0; j < dptc_params.dvfs_dptc_tables_ptr->wp_num; j++) {
+ sprintf(str,
+ "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ dcvr_row[j].dcvr_reg[0].AsInt,
+ dcvr_row[j].dcvr_reg[1].AsInt,
+ dcvr_row[j].dcvr_reg[2].AsInt,
+ dcvr_row[j].dcvr_reg[3].AsInt);
+
+ str += strlen(str);
+ }
+ }
+}
+
+/*!
+ * This function reads DVFS & DPTC translation table from user
+ *
+ * @param user_table pointer to user table
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_set_table(char *user_table)
+{
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+ char *tmp_str_ptr;
+ dvfs_dptc_tables_s *dptc_table;
+
+ if (dptc_params.dptc_is_active == TRUE) {
+ ret_val = -EINVAL;
+ return ret_val;
+ }
+
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ memset(tmp_str, 0, MAX_TABLE_SIZE);
+ tmp_str_ptr = tmp_str;
+
+ /*
+ * read num_of_wp and dvfs_state_num
+ * parameters from new table
+ */
+ while (tmp_str_ptr - tmp_str < MAX_TABLE_SIZE &&
+ (!copy_from_user(tmp_str_ptr, user_table, 1)) &&
+ tmp_str_ptr[0] != 0) {
+ tmp_str_ptr++;
+ user_table++;
+ }
+ if (tmp_str_ptr == tmp_str) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+didn't copy a character\n");
+ ret_val = -EFAULT;
+ } else if (tmp_str_ptr - tmp_str == MAX_TABLE_SIZE) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+read more than %d\n", MAX_TABLE_SIZE);
+ ret_val = -EFAULT;
+ } else {
+ /*
+ * copy table from user and set it as
+ * the current DPTC table
+ */
+ dptc_table = kmalloc(sizeof(dvfs_dptc_tables_s),
+ GFP_KERNEL);
+
+ if (!dptc_table) {
+ ret_val = -ENOMEM;
+ } else {
+ ret_val =
+ dvfs_dptc_parse_table(dptc_table, tmp_str);
+
+ if (ret_val == 0) {
+ free_dvfs_dptc_table();
+ dptc_params.dvfs_dptc_tables_ptr =
+ dptc_table;
+
+ set_dptc_wp(dptc_initial_wp);
+ }
+ }
+
+ }
+
+ vfree(tmp_str);
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This function is called when a ioctl call is made from user space.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ * @param cmd Ioctl command
+ * @param arg Ioctl argument
+ *
+ * Following are the ioctl commands for user to use:\n
+ * DPTC_IOCTENABLE : Enables the DPTC module.\n
+ * DPTC_IOCTDISABLE : Disables the DPTC module.\n
+ * DPTC_IOCSENABLERC : Enables DPTC reference circuits.\n
+ * DPTC_IOCSDISABLERC : Disables DPTC reference circuits.\n
+ * DPTC_IOCGETSTATE : Returns 1 if the DPTC module is enabled,
+ * returns 0 if the DPTC module is disabled.\n
+ * DPTC_IOCSWP : Sets working point.\n
+ * PM_IOCSTABLE : Sets translation table.\n
+ * PM_IOCGTABLE : Gets translation table.\n
+ * PM_IOCGFREQ : Returns current CPU frequency in Hz
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dptc_mx27_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct clk *clk;
+ unsigned int tmp;
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+
+ tmp = arg;
+
+ if (dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ down(&access_mutex);
+
+ pr_debug("DVFS_DPTC ioctl (%d)\n", cmd);
+
+ switch (cmd) {
+ /* Enable the DPTC module */
+ case DPTC_IOCTENABLE:
+ ret_val = start_dptc();
+ break;
+
+ /* Disable the DPTC module */
+ case DPTC_IOCTDISABLE:
+ ret_val = stop_dptc();
+ break;
+
+ case DPTC_IOCSENABLERC:
+ ret_val = enable_ref_circuits(tmp);
+ break;
+
+ case DPTC_IOCSDISABLERC:
+ ret_val = disable_ref_circuits(tmp);
+ break;
+ /*
+ * Return the DPTC module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DPTC_IOCGSTATE:
+ ret_val = dptc_params.dptc_is_active;
+ break;
+ case DPTC_IOCSWP:
+ if (dptc_params.dptc_is_active == FALSE) {
+ if (arg >= 0 &&
+ arg < dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ set_dptc_wp(arg);
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+
+ /* Update DPTC table */
+ case PM_IOCSTABLE:
+ ret_val = dvfs_dptc_set_table((char *)arg);
+ break;
+
+ case PM_IOCGTABLE:
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ dvfs_dptc_dump_table(tmp_str);
+ if (copy_to_user((char *)tmp, tmp_str, strlen(tmp_str))) {
+ printk(KERN_ERR
+ "Failed copy %d characters to 0x%x\n",
+ strlen(tmp_str), tmp);
+ ret_val = -EFAULT;
+ } else {
+ ret_val = 0;
+ }
+ vfree(tmp_str);
+ }
+ break;
+
+ case PM_IOCGFREQ:
+ clk = clk_get(NULL, "cpu_clk");
+ ret_val = clk_get_rate(clk);
+ break;
+
+ /* Unknown ioctl command -> return error */
+ default:
+ printk(KERN_ERR "Unknown ioctl command 0x%x\n", cmd);
+ ret_val = -ENOIOCTLCMD;
+ }
+
+ up(&access_mutex);
+
+ return ret_val;
+}
+
+/*!
+ * This function is the DPTC Interrupt handler.
+ * This function wakes-up the dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ *
+ * @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 dptc_mx27_irq(int irq, void *dev_id)
+{
+ if (dptc_params.dptc_is_active == TRUE) {
+ dptc_intr_status = __raw_readl(IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+
+ /* Acknowledge the interrupt */
+ __raw_writel(dptc_intr_status, IO_ADDRESS(MX27_PMCR_BASE_ADDR));
+
+ /* Extract the interrupt cause */
+ dptc_intr_status =
+ (dptc_intr_status >> MX27_PMCR_LO_INTR_OFFSET) & 0x7;
+
+ if (dptc_intr_status != 0) {
+ dptc_mask_dptc_int();
+ dptc_disable_dptc();
+ schedule_work(&dptc_work);
+ }
+ }
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_dptc_driver = {
+ .driver = {
+ .name = "mxc_dptc",
+ .bus = &platform_bus_type,
+ },
+ .suspend = mxc_dptc_suspend,
+ .resume = mxc_dptc_resume,
+};
+
+/*!
+ * This is platform device structure for adding MU
+ */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .id = 0,
+};
+
+/*!
+ * This function is called for module initialization.
+ * It initializes the driver data structures, sets up the DPTC hardware,
+ * registers the DPTC driver, creates a proc file system read entry and
+ * attaches the driver to the DPTC interrupt.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ *
+ */
+static int __init dptc_mx27_init(void)
+{
+ int res;
+ struct class_device *temp_class;
+
+ res = dvfs_dptc_init_default_table();
+
+ if (res < 0) {
+ printk(KERN_WARNING "Failed parsing default DPTC table\n");
+ return res;
+ }
+
+ /* Initialize DPTC hardware */
+ res = init_dptc_controller();
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ }
+
+ /* Initialize internal driver structures */
+ dptc_params.dptc_is_active = FALSE;
+
+ /*
+ * Register DPTC driver as a char driver with an automatically allocated
+ * major number.
+ */
+ major = register_chrdev(0, DEVICE_NAME, &fops);
+
+ /*
+ * Return error if a negative major number is returned.
+ */
+ if (major < 0) {
+ printk(KERN_ERR
+ "DPTC: Registering driver failed with %d\n", major);
+ free_dvfs_dptc_table();
+ return major;
+ }
+
+ mxc_dvfs_dptc_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(mxc_dvfs_dptc_class)) {
+ printk(KERN_ERR "DPTC: Error creating class.\n");
+ goto err_out;
+ }
+
+ temp_class =
+ class_device_create(mxc_dvfs_dptc_class, NULL, MKDEV(major, 0),
+ NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "DPTC: Error creating class device.\n");
+ goto err_out;
+ }
+
+ /* request the DPTC interrupt */
+ res = request_irq(INT_CCM, dptc_mx27_irq, 0, DEVICE_NAME, NULL);
+
+ /*
+ * If res is not 0, then where was an error
+ * during attaching to DPTC interrupt.
+ * Exit and return error code.
+ */
+ if (res) {
+ printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt");
+ free_dvfs_dptc_table();
+ goto err_out;
+ }
+
+ /* Register low power modes functions */
+ res = platform_driver_register(&mxc_dptc_driver);
+ if (res == 0) {
+ res = platform_device_register(&mxc_dptc_device);
+ if (res != 0) {
+ free_dvfs_dptc_table();
+ goto err_out;
+ }
+ }
+
+ dptc_params.suspended = FALSE;
+
+ return 0;
+
+ err_out:
+ printk(KERN_WARNING "MX27 DPTC driver was not initialized\n");
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+ return -1;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the DVFS & DPTC driver from kernel, frees the irq number
+ * and removes the proc file system entry.
+ */
+static void __exit dptc_mx27_cleanup(void)
+{
+ free_dvfs_dptc_table();
+
+ /* Un-register the driver and remove its node */
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+
+ /* release the DPTC interrupt */
+ free_irq(INT_CCM, NULL);
+
+ /* Unregister low power modes functions */
+ platform_driver_unregister(&mxc_dptc_driver);
+ platform_device_unregister(&mxc_dptc_device);
+
+ /* remove the DPTC proc file system entry */
+ remove_proc_entry(PROC_NODE_NAME, NULL);
+}
+
+module_init(dptc_mx27_init);
+module_exit(dptc_mx27_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MX27 DPTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pm/dvfs_dptc.c b/drivers/mxc/pm/dvfs_dptc.c
new file mode 100644
index 000000000000..d78cec974dae
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc.c
@@ -0,0 +1,1248 @@
+/*
+ * 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 dvfs_dptc.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC DVFS & DPTC module.
+ *
+ * The DVFS & DPTC driver
+ * driver is designed as a character driver which interacts with the MXC
+ * DVFS & DPTC hardware. Upon initialization, the DVFS & DPTC driver initializes
+ * the DVFS & DPTC hardware sets up driver nodes attaches to the DVFS & DPTC
+ * interrupts and initializes internal data structures. When the DVFS or DPTC
+ * interrupt occurs the driver checks the cause of the interrupt
+ * (lower voltage/frequency, increase voltage/frequency or emergency) and changes
+ * the CPU voltage and/or frequency according to translation table that is loaded
+ * into the driver (the voltage changes are done by calling some routines
+ * of the mc13783 driver).
+ *
+ * @ingroup PM_MX31 PM_MXC91321
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/proc_fs.h>
+#include <linux/jiffies.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pmic_external.h>
+
+/*
+ * Module header files
+ */
+#include "dvfs_dptc.h"
+#include <asm/arch/dptc.h>
+
+#ifdef CONFIG_MXC_DVFS
+#include <asm/arch/dvfs.h>
+#endif
+
+/*
+ * Prototypes
+ */
+static int dvfs_dptc_open(struct inode *inode, struct file *filp);
+static int dvfs_dptc_release(struct inode *inode, struct file *filp);
+static int dvfs_dptc_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_MXC_DVFS_SDMA
+static ssize_t dvfs_dptc_read(struct file *filp, char __user * buf,
+ size_t count, loff_t * ppos);
+#endif
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+static irqreturn_t dvfs_dptc_irq(int irq, void *dev_id);
+#else
+static void dvfs_dptc_sdma_callback(dvfs_dptc_params_s * params);
+#endif
+
+#ifdef CONFIG_MXC_DPTC
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_dptc_driver = {
+ .driver = {
+ .name = "mxc_dptc",
+ },
+ .suspend = mxc_dptc_suspend,
+ .resume = mxc_dptc_resume,
+};
+
+/*!
+ * This is platform device structure for adding MU
+ */
+static struct platform_device mxc_dptc_device = {
+ .name = "mxc_dptc",
+ .id = 0,
+};
+#endif
+
+/*
+ * Global variables
+ */
+
+/*!
+ * The dvfs_dptc_params structure holds all the internal DPTC driver parameters
+ * (current working point, current frequency, translation table and DPTC
+ * log buffer).
+ */
+static dvfs_dptc_params_s dvfs_dptc_params;
+
+/*!
+ * Holds the automatically selected DPTC driver major number.
+ */
+static int major;
+
+static struct class *mxc_dvfs_dptc_class;
+
+/*
+ * This mutex makes the Read,Write and IOCTL command mutual exclusive.
+ */
+DECLARE_MUTEX(access_mutex);
+
+/*!
+ * This structure contains pointers for device driver entry point.
+ * The driver register function in init module will call this
+ * structure.
+ */
+static struct file_operations fops = {
+ .open = dvfs_dptc_open,
+ .release = dvfs_dptc_release,
+ .ioctl = dvfs_dptc_ioctl,
+#ifdef CONFIG_MXC_DVFS_SDMA
+ .read = dvfs_dptc_read,
+#endif
+};
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*
+ * Update pointers to physical addresses of DVFS & DPTC table
+ * for SDMA usage
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DVFS &
+ * DPTC translation table.
+ */
+static void dvfs_dptc_virt_2_phys(dvfs_dptc_tables_s * dvfs_dptc_table)
+{
+ int i;
+
+ /* Update DCVR pointers */
+ for (i = 0; i < dvfs_dptc_table->dvfs_state_num; i++) {
+ dvfs_dptc_table->dcvr[i] = (dcvr_state *)
+ sdma_virt_to_phys(dvfs_dptc_table->dcvr[i]);
+ }
+ dvfs_dptc_table->dcvr = (dcvr_state **)
+ sdma_virt_to_phys(dvfs_dptc_table->dcvr);
+ dvfs_dptc_table->table = (dvfs_state *)
+ sdma_virt_to_phys(dvfs_dptc_table->table);
+}
+
+/*
+ * Update pointers to virtual addresses of DVFS & DPTC table
+ * for ARM usage
+ *
+ * @param dvfs_dptc_tables_ptr pointer to the DVFS &
+ * DPTC translation table.
+ */
+static void dvfs_dptc_phys_2_virt(dvfs_dptc_tables_s * dvfs_dptc_table)
+{
+ int i;
+
+ dvfs_dptc_table->table = sdma_phys_to_virt
+ ((unsigned long)dvfs_dptc_table->table);
+ dvfs_dptc_table->dcvr = sdma_phys_to_virt
+ ((unsigned long)dvfs_dptc_table->dcvr);
+
+ /* Update DCVR pointers */
+ for (i = 0; i < dvfs_dptc_table->dvfs_state_num; i++) {
+ dvfs_dptc_table->dcvr[i] =
+ sdma_phys_to_virt((unsigned long)dvfs_dptc_table->dcvr[i]);
+ }
+}
+#endif
+
+/*!
+ * This function frees power management table structures
+ */
+static void free_dvfs_dptc_table(void)
+{
+ int i;
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dvfs_dptc_phys_2_virt(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+#endif
+
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr[i]);
+ }
+
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr);
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->table);
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr->wp);
+
+ sdma_free(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+
+ dvfs_dptc_params.dvfs_dptc_tables_ptr = 0;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * reads the next line of the table in text format
+ *
+ * @param str pointer to the previous line
+ *
+ * @return pointer to the next line
+ */
+static char *pm_table_get_next_line(char *str)
+{
+ char *line_ptr;
+ int flag = 0;
+
+ if (strlen(str) == 0)
+ return str;
+
+ line_ptr = strchr(str, '\n') + 1;
+
+ while (!flag) {
+ if (strlen(line_ptr) == 0) {
+ flag = 1;
+ } else if (line_ptr[0] == '\n') {
+ line_ptr++;
+ } else if (line_ptr[0] == '#') {
+ line_ptr = pm_table_get_next_line(line_ptr);
+ } else {
+ flag = 1;
+ }
+ }
+
+ return line_ptr;
+}
+
+/*
+ * DVFS & DPTC table parsing function
+ * sets the values of DVFS & DPTC tables from
+ * table in text format
+ *
+ * @param pm_table pointer to the table in binary format
+ * @param pm_str pointer to the table in text format
+ *
+ * @return 0 on success, error code on failure
+ */
+static int dvfs_dptc_parse_table(dvfs_dptc_tables_s * pm_table, char *pm_str)
+{
+ char *pm_str_ptr;
+ int i, j, n;
+ dptc_wp *wp;
+ dvfs_state *table;
+
+ pm_str_ptr = pm_str;
+
+ n = sscanf(pm_str_ptr, "WORKING POINT %d\n", &pm_table->wp_num);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WORKING POINT number\n");
+ return -1;
+ }
+
+ pm_table->curr_wp = 0;
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ pm_table->dvfs_state_num = 4;
+ pm_table->use_four_freq = 1;
+ } else {
+ pm_table->dvfs_state_num = 1;
+ }
+
+ pm_table->wp =
+ (dptc_wp *) sdma_malloc(sizeof(dptc_wp) * pm_table->wp_num);
+ if (!pm_table->wp) {
+ printk(KERN_ERR "Failed allocating memory\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->wp_num; i++) {
+
+ wp = &pm_table->wp[i];
+
+ wp->wp_index = i;
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ n = sscanf(pm_str_ptr, "WP 0x%x 0x%x 0x%x 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0],
+ (unsigned int *)&wp->pmic_values[1],
+ (unsigned int *)&wp->pmic_values[2],
+ (unsigned int *)&wp->pmic_values[3]);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+ } else {
+ n = sscanf(pm_str_ptr, "WP 0x%x\n",
+ (unsigned int *)&wp->pmic_values[0]);
+
+ if (n != 1) {
+ printk(KERN_WARNING "Failed read WP %d\n", i);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+
+ }
+
+ pm_table->table =
+ (dvfs_state *) sdma_malloc(sizeof(dvfs_state) *
+ pm_table->dvfs_state_num);
+
+ if (!pm_table->table) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ sdma_free(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ table = &pm_table->table[i];
+
+ n = sscanf(pm_str_ptr,
+ "FREQ %d %d 0x%x 0x%x 0x%x 0x%x %d\n",
+ (unsigned int *)&table->pll_sw_up,
+ (unsigned int *)&table->pll_sw_down,
+ (unsigned int *)&table->pdr0_up,
+ (unsigned int *)&table->pdr0_down,
+ (unsigned int *)&table->pll_up,
+ (unsigned int *)&table->pll_down,
+ (unsigned int *)&table->vscnt);
+
+ if (n != 7) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ pm_table->dcvr =
+ (dcvr_state **) sdma_malloc(sizeof(dcvr_state *) *
+ pm_table->dvfs_state_num);
+
+ if (!pm_table->dcvr) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < pm_table->dvfs_state_num; i++) {
+ pm_table->dcvr[i] =
+ (dcvr_state *) sdma_malloc(sizeof(dcvr_state) *
+ pm_table->wp_num);
+
+ if (!pm_table->dcvr[i]) {
+ printk(KERN_WARNING "Failed allocating memory\n");
+
+ for (j = i - 1; j >= 0; j--) {
+ sdma_free(pm_table->dcvr[j]);
+ }
+
+ sdma_free(pm_table->dcvr);
+ return -ENOMEM;
+ }
+
+ for (j = 0; j < pm_table->wp_num; j++) {
+
+ n = sscanf(pm_str_ptr, "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ &pm_table->dcvr[i][j].dcvr_reg[0].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[1].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[2].AsInt,
+ &pm_table->dcvr[i][j].dcvr_reg[3].AsInt);
+
+ if (n != 4) {
+ printk(KERN_WARNING "Failed read FREQ %d\n", i);
+
+ for (j = i; j >= 0; j--) {
+ sdma_free(pm_table->dcvr[j]);
+ }
+ sdma_free(pm_table->dcvr);
+ sdma_free(pm_table->table);
+ sdma_free(pm_table->wp);
+ return -1;
+ }
+
+ pm_str_ptr = pm_table_get_next_line(pm_str_ptr);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Initializes the default values of DVFS & DPTC table
+ *
+ * @return 0 on success, error code on failure
+ */
+static int __init dvfs_dptc_init_default_table(void)
+{
+ int res = 0;
+ char *table_str;
+ struct clk *clk;
+
+ dvfs_dptc_tables_s *default_table;
+
+ default_table = sdma_malloc(sizeof(dvfs_dptc_tables_s));
+
+ if (!default_table) {
+ return -ENOMEM;
+ }
+
+ table_str = default_table_str;
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ if (cpu_is_mx31_rev(CHIP_REV_2_0) < 0) {
+ clk = clk_get(NULL, "ckih");
+ if (clk_get_rate(clk) == 27000000) {
+ printk(KERN_INFO
+ "DVFS & DPTC: using 27MHz CKIH table\n");
+#ifdef CONFIG_ARCH_MX3
+ table_str = default_table_str_27ckih;
+#endif
+ }
+ } else {
+#ifdef CONFIG_ARCH_MX3
+ table_str = default_table_str_rev2;
+#endif
+ }
+ clk_put(clk);
+ }
+
+ memset(default_table, 0, sizeof(dvfs_dptc_tables_s));
+ res = dvfs_dptc_parse_table(default_table, table_str);
+
+ if (res == 0) {
+ dvfs_dptc_params.dvfs_dptc_tables_ptr = default_table;
+ }
+
+ return res;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+/*!
+ * This function is called for SDMA channel initialization.
+ *
+ * @param params pointer to the DPTC driver parameters structure.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ */
+static int init_sdma_channel(dvfs_dptc_params_s * params)
+{
+ dma_channel_params sdma_params;
+ dma_request_t sdma_request;
+ int i;
+ int res = 0;
+
+ params->sdma_channel = 0;
+ res = mxc_request_dma(&params->sdma_channel, "DVFS_DPTC");
+ if (res < 0) {
+ printk(KERN_ERR "Failed allocate SDMA channel for DVFS_DPTC\n");
+ return res;
+ }
+
+ memset(&sdma_params, 0, sizeof(dma_channel_params));
+ sdma_params.peripheral_type = CCM;
+ sdma_params.transfer_type = per_2_emi;
+ sdma_params.event_id = DMA_REQ_CCM;
+ sdma_params.callback = (dma_callback_t) dvfs_dptc_sdma_callback;
+ sdma_params.arg = params;
+ sdma_params.per_address = CCM_BASE_ADDR;
+ sdma_params.watermark_level =
+ sdma_virt_to_phys(params->dvfs_dptc_tables_ptr);
+ sdma_params.bd_number = 2;
+
+ res = mxc_dma_setup_channel(params->sdma_channel, &sdma_params);
+
+ if (res == 0) {
+ memset(&sdma_request, 0, sizeof(dma_request_t));
+
+ for (i = 0; i < DVFS_LB_SDMA_BD; i++) {
+ sdma_request.destAddr = (__u8 *)
+ (params->dvfs_log_buffer_phys +
+ i * (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) /
+ DVFS_LB_SDMA_BD);
+ sdma_request.count = DVFS_LB_SIZE / DVFS_LB_SDMA_BD;
+ sdma_request.bd_cont = 1;
+
+ mxc_dma_set_config(params->sdma_channel, &sdma_request,
+ i);
+ }
+
+ mxc_dma_start(params->sdma_channel);
+ }
+
+ return res;
+}
+#endif
+
+/*!
+ * This function is called for module initialization.
+ * It initializes the driver data structures, sets up the DPTC hardware,
+ * registers the DPTC driver, creates a proc file system read entry and
+ * attaches the driver to the DPTC interrupt.
+ *
+ * @return 0 to indicate success else returns a negative number.
+ *
+ */
+static int __init dvfs_dptc_init(void)
+{
+ int res = 0;
+ struct class_device *temp_class;
+
+ res = dvfs_dptc_init_default_table();
+
+ if (res < 0) {
+ printk(KERN_WARNING "Failed parsing default DPTC table\n");
+ return res;
+ }
+#ifdef CONFIG_MXC_DPTC
+ /* Initialize DPTC hardware */
+ res = init_dptc_controller(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+ /* Initialize DVFS hardware */
+ res = init_dvfs_controller(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+
+ /* Enable 4 mc13783 output voltages */
+ pmic_write_reg(REG_ARBITRATION_SWITCHERS, 1, (1 << 5));
+
+ /* Enable mc13783 voltage ready signal */
+ pmic_write_reg(REG_INTERRUPT_MASK_1, 0, (1 << 11));
+
+ /* Set mc13783 DVS speed 25mV each 4us */
+ pmic_write_reg(REG_SWITCHERS_4, 1, (1 << 6));
+ pmic_write_reg(REG_SWITCHERS_4, 0, (1 << 7));
+
+ dvfs_update_freqs_table(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+#endif
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ /* Update addresses to physical */
+ if (res == 0) {
+ dvfs_dptc_virt_2_phys(dvfs_dptc_params.dvfs_dptc_tables_ptr);
+ }
+
+ res = init_sdma_channel(&dvfs_dptc_params);
+ if (res < 0) {
+ free_dvfs_dptc_table();
+ return res;
+ }
+#endif
+
+ /* Initialize internal driver structures */
+ dvfs_dptc_params.dptc_is_active = FALSE;
+
+#ifdef CONFIG_MXC_DVFS
+ dvfs_dptc_params.dvfs_is_active = FALSE;
+#endif
+
+ /*
+ * Register DPTC driver as a char driver with an automatically allocated
+ * major number.
+ */
+ major = register_chrdev(0, DEVICE_NAME, &fops);
+
+ /*
+ * Return error if a negative major number is returned.
+ */
+ if (major < 0) {
+ printk(KERN_ERR
+ "DPTC: Registering driver failed with %d\n", major);
+ free_dvfs_dptc_table();
+ return major;
+ }
+
+ mxc_dvfs_dptc_class = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(mxc_dvfs_dptc_class)) {
+ printk(KERN_ERR "DPTC: Error creating class.\n");
+ res = PTR_ERR(mxc_dvfs_dptc_class);
+ goto err_out1;
+ }
+
+ temp_class =
+ class_device_create(mxc_dvfs_dptc_class, NULL, MKDEV(major, 0),
+ NULL, DEVICE_NAME);
+ if (IS_ERR(temp_class)) {
+ printk(KERN_ERR "DPTC: Error creating class device.\n");
+ res = PTR_ERR(temp_class);
+ goto err_out2;
+ }
+#ifndef CONFIG_MXC_DVFS_SDMA
+ /* request the DPTC interrupt */
+ res = request_irq(INT_CCM, dvfs_dptc_irq, 0, DEVICE_NAME, NULL);
+ /*
+ * If res is not 0, then where was an error
+ * during attaching to DPTC interrupt.
+ * Exit and return error code.
+ */
+ if (res) {
+ printk(KERN_ERR "DPTC: Unable to attach to DPTC interrupt");
+ goto err_out3;
+ }
+ /* request the DVFS interrupt */
+ res = request_irq(INT_DVFS, dvfs_dptc_irq, 0, DEVICE_NAME, NULL);
+ if (res) {
+ printk(KERN_ERR "DVFS: Unable to attach to DVFS interrupt");
+ goto err_out4;
+ }
+#endif
+
+#ifdef CONFIG_MXC_DPTC
+ /* Register low power modes functions */
+ res = platform_driver_register(&mxc_dptc_driver);
+ if (res == 0) {
+ res = platform_device_register(&mxc_dptc_device);
+ if (res != 0) {
+ goto err_out5;
+ }
+ }
+#endif
+ dvfs_dptc_params.suspended = FALSE;
+
+ return res;
+
+ err_out5:
+ free_irq(INT_DVFS, NULL);
+#ifndef CONFIG_MXC_DVFS_SDMA
+ err_out4:
+#endif
+ free_irq(INT_CCM, NULL);
+#ifndef CONFIG_MXC_DVFS_SDMA
+ err_out3:
+#endif
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ err_out2:
+ class_destroy(mxc_dvfs_dptc_class);
+ err_out1:
+ unregister_chrdev(major, DEVICE_NAME);
+ free_dvfs_dptc_table();
+ printk(KERN_ERR "DVFS&DPTC driver was not initialized\n");
+ return res;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the DVFS & DPTC driver from kernel, frees the irq number
+ * and removes the proc file system entry.
+ */
+static void __exit dvfs_dptc_cleanup(void)
+{
+#ifdef CONFIG_MXC_DPTC
+ /* Unregister low power modes functions */
+ platform_driver_unregister(&mxc_dptc_driver);
+ platform_device_unregister(&mxc_dptc_device);
+#endif
+
+ free_dvfs_dptc_table();
+
+ /* Un-register the driver and remove its node */
+ class_device_destroy(mxc_dvfs_dptc_class, MKDEV(major, 0));
+ class_destroy(mxc_dvfs_dptc_class);
+ unregister_chrdev(major, DEVICE_NAME);
+
+ /* release the DPTC interrupt */
+ free_irq(INT_CCM, NULL);
+ /* release the DVFS interrupt */
+ free_irq(INT_DVFS, NULL);
+
+ /* remove the DPTC proc file system entry */
+ remove_proc_entry(PROC_NODE_NAME, NULL);
+}
+
+/*!
+ * This function is called when the driver is opened. This function
+ * checks if the user that open the device has root privileges.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dvfs_dptc_open(struct inode *inode, struct file *filp)
+{
+ /*
+ * check if the program that opened the driver has root
+ * privileges, if not return error.
+ */
+ if (!capable(CAP_SYS_ADMIN)) {
+ return -EACCES;
+ }
+
+ if (dvfs_dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*!
+ * This function is called when the driver is close.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ *
+ */
+static int dvfs_dptc_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * This function dumps dptc translation table into string pointer
+ *
+ * @param str string pointer
+ */
+static void dvfs_dptc_dump_table(char *str)
+{
+ int i, j;
+ dcvr_state **dcvr_arr;
+ dcvr_state *dcvr_row;
+ dvfs_state *table;
+
+ memset(str, 0, MAX_TABLE_SIZE);
+
+ sprintf(str, "WORKING POINT %d\n",
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num);
+ str += strlen(str);
+
+ for (i = 0; i < dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num; i++) {
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ sprintf(str, "WP 0x%x 0x%x 0x%x 0x%x\n", (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[0], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[1], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[2], (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[3]);
+ } else {
+ sprintf(str, "WP 0x%x\n", (unsigned int)
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp[i].
+ pmic_values[0]);
+ }
+
+ str += strlen(str);
+ }
+
+ if (cpu_is_mx31() || cpu_is_mx32()) {
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num;
+ i++) {
+ table = dvfs_dptc_params.dvfs_dptc_tables_ptr->table;
+#ifdef CONFIG_MXC_DVFS_SDMA
+ table = sdma_phys_to_virt((unsigned long)table);
+#endif
+ sprintf(str,
+ "FREQ %d %d 0x%x 0x%x 0x%x 0x%x %d\n",
+ (unsigned int)table[i].pll_sw_up,
+ (unsigned int)table[i].pll_sw_down,
+ (unsigned int)table[i].pdr0_up,
+ (unsigned int)table[i].pdr0_down,
+ (unsigned int)table[i].pll_up,
+ (unsigned int)table[i].pll_down,
+ (unsigned int)table[i].vscnt);
+
+ str += strlen(str);
+ }
+ }
+
+ for (i = 0;
+ i < dvfs_dptc_params.dvfs_dptc_tables_ptr->dvfs_state_num; i++) {
+ dcvr_arr = dvfs_dptc_params.dvfs_dptc_tables_ptr->dcvr;
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dcvr_arr = sdma_phys_to_virt((unsigned long)dcvr_arr);
+#endif
+ dcvr_row = dcvr_arr[i];
+#ifdef CONFIG_MXC_DVFS_SDMA
+ dcvr_row = sdma_phys_to_virt((unsigned long)dcvr_row);
+#endif
+
+ for (j = 0;
+ j < dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num; j++) {
+ sprintf(str,
+ "DCVR 0x%x 0x%x 0x%x 0x%x\n",
+ dcvr_row[j].dcvr_reg[0].AsInt,
+ dcvr_row[j].dcvr_reg[1].AsInt,
+ dcvr_row[j].dcvr_reg[2].AsInt,
+ dcvr_row[j].dcvr_reg[3].AsInt);
+
+ str += strlen(str);
+ }
+ }
+}
+
+/*!
+ * This function reads DVFS & DPTC translation table from user
+ *
+ * @param user_table pointer to user table
+ * @return 0 on success, error code on failure
+ */
+int dvfs_dptc_set_table(char *user_table)
+{
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+ char *tmp_str_ptr;
+ dvfs_dptc_tables_s *dptc_table;
+
+ if ((cpu_is_mx31() || cpu_is_mx32()) &&
+ (dvfs_dptc_params.dptc_is_active == TRUE ||
+ dvfs_dptc_params.dvfs_is_active == TRUE)) {
+ ret_val = -EINVAL;
+ return ret_val;
+ } else if (dvfs_dptc_params.dptc_is_active == TRUE) {
+ ret_val = -EINVAL;
+ return ret_val;
+ }
+
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ memset(tmp_str, 0, MAX_TABLE_SIZE);
+ tmp_str_ptr = tmp_str;
+
+ /*
+ * read num_of_wp and dvfs_state_num
+ * parameters from new table
+ */
+ while (tmp_str_ptr - tmp_str < MAX_TABLE_SIZE &&
+ (!copy_from_user(tmp_str_ptr, user_table, 1)) &&
+ tmp_str_ptr[0] != 0) {
+ tmp_str_ptr++;
+ user_table++;
+ }
+ if (tmp_str_ptr == tmp_str) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+didn't copy a character\n");
+ ret_val = -EFAULT;
+ } else if (tmp_str_ptr - tmp_str == MAX_TABLE_SIZE) {
+ /* error reading from table */
+ printk(KERN_ERR "Failed reading table from user, \
+read more than %d\n", MAX_TABLE_SIZE);
+ ret_val = -EFAULT;
+ } else {
+ /*
+ * copy table from user and set it as
+ * the current DPTC table
+ */
+ dptc_table = sdma_malloc(sizeof(dvfs_dptc_tables_s));
+
+ if (!dptc_table) {
+ ret_val = -ENOMEM;
+ } else {
+ ret_val =
+ dvfs_dptc_parse_table(dptc_table, tmp_str);
+
+ if (ret_val == 0) {
+ free_dvfs_dptc_table();
+ dvfs_dptc_params.dvfs_dptc_tables_ptr =
+ dptc_table;
+
+#ifdef CONFIG_MXC_DVFS
+ dvfs_update_freqs_table
+ (dvfs_dptc_params.
+ dvfs_dptc_tables_ptr);
+#endif
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+ /* Update addresses to physical */
+ dvfs_dptc_virt_2_phys(dvfs_dptc_params.
+ dvfs_dptc_tables_ptr);
+ mxc_free_dma(dvfs_dptc_params.
+ sdma_channel);
+ init_sdma_channel(&dvfs_dptc_params);
+#endif
+#ifdef CONFIG_MXC_DPTC
+ set_dptc_curr_freq(&dvfs_dptc_params,
+ 0);
+ set_dptc_wp(&dvfs_dptc_params, 0);
+#endif
+ }
+ }
+
+ }
+
+ vfree(tmp_str);
+ }
+
+ return ret_val;
+}
+
+#ifdef CONFIG_MXC_DVFS_SDMA
+static ssize_t dvfs_dptc_read(struct file *filp, char __user * buf,
+ size_t count, loff_t * ppos)
+{
+ size_t count0, count1;
+
+ while (dvfs_dptc_params.chars_in_buffer < count) {
+ //count = dvfs_dptc_params.chars_in_buffer;
+ waitqueue_active(&dvfs_dptc_params.dvfs_pred_wait);
+ wake_up(&dvfs_dptc_params.dvfs_pred_wait);
+ schedule();
+ }
+
+ if (dvfs_dptc_params.read_ptr + count <
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) {
+ count0 = count;
+ count1 = 0;
+ } else {
+ count0 =
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8 -
+ dvfs_dptc_params.read_ptr;
+ count1 = count - count0;
+ }
+
+ copy_to_user(buf, dvfs_dptc_params.read_ptr, count0);
+ copy_to_user(buf + count0, dvfs_dptc_params.dvfs_log_buffer, count1);
+
+ if (count1 == 0) {
+ dvfs_dptc_params.read_ptr += count;
+ } else {
+ dvfs_dptc_params.read_ptr =
+ dvfs_dptc_params.dvfs_log_buffer + count1;
+ }
+
+ if (dvfs_dptc_params.read_ptr ==
+ dvfs_dptc_params.dvfs_log_buffer +
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) {
+ dvfs_dptc_params.read_ptr = dvfs_dptc_params.dvfs_log_buffer;
+ }
+
+ dvfs_dptc_params.chars_in_buffer -= count;
+
+ return count;
+}
+#endif
+
+/*!
+ * This function is called when a ioctl call is made from user space.
+ *
+ * @param inode Pointer to device inode
+ * @param filp Pointer to device file structure
+ * @param cmd Ioctl command
+ * @param arg Ioctl argument
+ *
+ * Following are the ioctl commands for user to use:\n
+ * DPTC_IOCTENABLE : Enables the DPTC module.\n
+ * DPTC_IOCTDISABLE : Disables the DPTC module.\n
+ * DPTC_IOCSENABLERC : Enables DPTC reference circuits.\n
+ * DPTC_IOCSDISABLERC : Disables DPTC reference circuits.\n
+ * DPTC_IOCGETSTATE : Returns 1 if the DPTC module is enabled,
+ * returns 0 if the DPTC module is disabled.\n
+ * DPTC_IOCSWP : Sets working point.\n
+ * PM_IOCSTABLE : Sets translation table.\n
+ * PM_IOCGTABLE : Gets translation table.\n
+ * DVFS_IOCTENABLE : Enables DVFS
+ * DVFS_IOCTDISABLE : Disables DVFS
+ * DVFS_IOCGSTATE : Returns 1 if the DVFS module is enabled,
+ * returns 0 if the DVFS module is disabled.\n
+ * DVFS_IOCSSWGP : Sets the value of DVFS SW general
+ * purpose bits.\n
+ * DVFS_IOCSWFI : Sets the status of WFI monitoring.\n
+ * PM_IOCGFREQ : Returns current CPU frequency in Hz
+ * DVFS_IOCSFREQ : Sets DVFS frequency when DVFS\n
+ * HW is disabled.\n
+ *
+ * @return The function returns 0 on success and a non-zero value on
+ * failure.
+ */
+static int dvfs_dptc_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct clk *clk;
+ unsigned int tmp;
+ int ret_val = -ENOIOCTLCMD;
+ char *tmp_str;
+
+ tmp = arg;
+
+ if (dvfs_dptc_params.suspended) {
+ return -EPERM;
+ }
+
+ down(&access_mutex);
+
+ pr_debug("DVFS_DPTC ioctl (%d)\n", cmd);
+
+ switch (cmd) {
+#ifdef CONFIG_MXC_DPTC
+ /* Enable the DPTC module */
+ case DPTC_IOCTENABLE:
+ ret_val = start_dptc(&dvfs_dptc_params);
+ break;
+
+ /* Disable the DPTC module */
+ case DPTC_IOCTDISABLE:
+ ret_val = stop_dptc(&dvfs_dptc_params);
+ break;
+
+ case DPTC_IOCSENABLERC:
+ ret_val = enable_ref_circuits(&dvfs_dptc_params, tmp);
+ break;
+
+ case DPTC_IOCSDISABLERC:
+ ret_val = disable_ref_circuits(&dvfs_dptc_params, tmp);
+ break;
+ /*
+ * Return the DPTC module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DPTC_IOCGSTATE:
+ ret_val = dvfs_dptc_params.dptc_is_active;
+ break;
+ case DPTC_IOCSWP:
+ if (dvfs_dptc_params.dptc_is_active == FALSE) {
+ if (arg >= 0 &&
+ arg <
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->wp_num) {
+ set_dptc_wp(&dvfs_dptc_params, arg);
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+
+#endif /* CONFIG_MXC_DPTC */
+
+ /* Update DPTC table */
+ case PM_IOCSTABLE:
+ ret_val = dvfs_dptc_set_table((char *)arg);
+ break;
+
+ case PM_IOCGTABLE:
+ tmp_str = vmalloc(MAX_TABLE_SIZE);
+ if (tmp_str < 0) {
+ ret_val = (int)tmp_str;
+ } else {
+ dvfs_dptc_dump_table(tmp_str);
+ if (copy_to_user((char *)tmp, tmp_str, strlen(tmp_str))) {
+ printk(KERN_ERR
+ "Failed copy %d characters to 0x%x\n",
+ strlen(tmp_str), tmp);
+ ret_val = -EFAULT;
+ } else {
+ ret_val = 0;
+ }
+ vfree(tmp_str);
+ }
+ break;
+
+#ifdef CONFIG_MXC_DVFS
+ /* Enable the DVFS module */
+ case DVFS_IOCTENABLE:
+ ret_val = start_dvfs(&dvfs_dptc_params);
+ break;
+
+ /* Disable the DVFS module */
+ case DVFS_IOCTDISABLE:
+ ret_val = stop_dvfs(&dvfs_dptc_params);
+ break;
+ /*
+ * Return the DVFS module current state.
+ * Returns 1 if the DPTC module is enabled, else returns 0
+ */
+ case DVFS_IOCGSTATE:
+ ret_val = dvfs_dptc_params.dvfs_is_active;
+ break;
+ case DVFS_IOCSSWGP:
+ ret_val = set_sw_gp((unsigned char)arg);
+ break;
+ case DVFS_IOCSWFI:
+ ret_val = set_wfi((unsigned char)arg);
+ break;
+ case DVFS_IOCSFREQ:
+ if (dvfs_dptc_params.dvfs_is_active == FALSE ||
+ dvfs_dptc_params.dvfs_mode == DVFS_PRED_MODE) {
+ if (arg >= 0 &&
+ arg <
+ dvfs_dptc_params.dvfs_dptc_tables_ptr->
+ dvfs_state_num) {
+ ret_val = dvfs_set_state(arg);
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+ break;
+ case DVFS_IOCSMODE:
+#ifdef CONFIG_MXC_DVFS_SDMA
+ if (dvfs_dptc_params.dvfs_is_active == FALSE) {
+ if ((unsigned int)arg == DVFS_HW_MODE ||
+ (unsigned int)arg == DVFS_PRED_MODE) {
+ dvfs_dptc_params.dvfs_mode = (unsigned int)arg;
+ ret_val = 0;
+ } else {
+ ret_val = -EINVAL;
+ }
+ } else {
+ ret_val = -EINVAL;
+ }
+#else
+ /* Predictive mode is supported only in SDMA mode */
+ ret_val = -EINVAL;
+#endif
+ break;
+#endif /* CONFIG_MXC_DVFS */
+ case PM_IOCGFREQ:
+ clk = clk_get(NULL, "cpu_clk");
+ ret_val = clk_get_rate(clk);
+ break;
+
+ /* Unknown ioctl command -> return error */
+ default:
+ printk(KERN_ERR "Unknown ioctl command 0x%x\n", cmd);
+ ret_val = -ENOIOCTLCMD;
+ }
+
+ up(&access_mutex);
+
+ return ret_val;
+}
+
+#ifndef CONFIG_MXC_DVFS_SDMA
+/*!
+ * This function is the DPTC & DVFS Interrupt handler.
+ * This function wakes-up the dvfs_dptc_workqueue_handler function that handles the
+ * DPTC interrupt.
+ *
+ * @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 dvfs_dptc_irq(int irq, void *dev_id)
+{
+
+#ifdef CONFIG_MXC_DPTC
+ if (dvfs_dptc_params.dptc_is_active == TRUE) {
+ dptc_irq();
+ }
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+ if (dvfs_dptc_params.dvfs_is_active == TRUE) {
+ dvfs_irq(&dvfs_dptc_params);
+ }
+#endif
+
+ return IRQ_RETVAL(1);
+}
+#else
+/*!
+ * This function is the DPTC & DVFS SDMA callback.
+ *
+ * @param params pointer to the DVFS & DPTC driver parameters structure.
+ */
+static void dvfs_dptc_sdma_callback(dvfs_dptc_params_s * params)
+{
+ dma_request_t sdma_request_params;
+ int i;
+
+ for (i = 0; i < DVFS_LB_SDMA_BD; i++) {
+ mxc_dma_get_config(params->sdma_channel,
+ &sdma_request_params, i);
+
+ if (sdma_request_params.bd_error == 1) {
+ printk(KERN_WARNING
+ "Error in DVFS-DPTC buffer descriptor\n");
+ }
+
+ if (sdma_request_params.bd_done == 0) {
+ params->chars_in_buffer +=
+ (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8) /
+ DVFS_LB_SDMA_BD;
+
+ if (params->chars_in_buffer >
+ (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8)) {
+ params->chars_in_buffer =
+ DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE / 8;
+ params->read_ptr = params->dvfs_log_buffer;
+ }
+
+ sdma_request_params.destAddr =
+ (__u8 *) (params->dvfs_log_buffer_phys +
+ i * (DVFS_LB_SIZE * DVFS_LB_SAMPLE_SIZE /
+ 8) / DVFS_LB_SDMA_BD);
+ sdma_request_params.count =
+ DVFS_LB_SIZE / DVFS_LB_SDMA_BD;
+ sdma_request_params.bd_cont = 1;
+ mxc_dma_set_config(params->sdma_channel,
+ &sdma_request_params, i);
+
+ if (params->dvfs_mode == DVFS_PRED_MODE) {
+ wake_up_interruptible(&params->dvfs_pred_wait);
+ }
+ }
+ }
+
+#ifdef CONFIG_MXC_DPTC
+ if (params->prev_wp != params->dvfs_dptc_tables_ptr->curr_wp) {
+ dptc_irq();
+ }
+#endif
+}
+#endif
+
+module_init(dvfs_dptc_init);
+module_exit(dvfs_dptc_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("DVFS & DPTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mxc/pm/dvfs_dptc.h b/drivers/mxc/pm/dvfs_dptc.h
new file mode 100644
index 000000000000..8f8d5fae3087
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc.h
@@ -0,0 +1,83 @@
+/*
+ * 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 dvfs_dptc.h
+ *
+ * @brief MXC dvfs & dptc header file.
+ *
+ * @ingroup PM_MX27 PM_MX31 PM_MXC91321
+ */
+#ifndef __DVFS_DPTC_H__
+#define __DVFS_DPTC_H__
+
+#include <asm/arch/pm_api.h>
+#include <asm/hardware.h>
+#include <asm/arch/dvfs_dptc_struct.h>
+
+#ifdef CONFIG_ARCH_MX27
+#include <asm/arch/dma.h>
+#else
+#include <asm/arch/sdma.h>
+#endif
+
+#ifdef CONFIG_ARCH_MX3
+#include "dvfs_dptc_table_mx31.h"
+#include "dvfs_dptc_table_mx31_27ckih.h"
+#include "dvfs_dptc_table_mx31_rev2.h"
+#endif
+#ifdef CONFIG_ARCH_MXC91321
+#include "dvfs_dptc_table_mxc91321.h"
+#endif
+#ifdef CONFIG_ARCH_MX27
+#include "dvfs_dptc_table_mx27.h"
+#endif
+
+#ifdef CONFIG_MXC_DVFS
+#ifndef CONFIG_MXC_DPTC
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dvfs"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dvfs"
+#endif /* ifndef CONFIG_MXC_DPTC */
+#ifdef CONFIG_MXC_DPTC
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dvfs_dptc"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dvfs_dptc"
+#endif /* ifdef CONFIG_MXC_DPTC */
+#else /* ifdef CONFIG_MXC_DVFS */
+/*!
+ * DPTC Module Name
+ */
+#define DEVICE_NAME "dptc"
+
+/*!
+ * DPTC driver node Name
+ */
+#define NODE_NAME "dptc"
+#endif /* ifdef CONFIG_MXC_DVFS */
+
+#define MAX_TABLE_SIZE 8192
+
+#endif /* __DPTC_H__ */
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx27.h b/drivers/mxc/pm/dvfs_dptc_table_mx27.h
new file mode 100644
index 000000000000..422db4b2c76c
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx27.h
@@ -0,0 +1,69 @@
+/*
+ * 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 dptc.h
+ *
+ * @brief i.MX27 dptc table file.
+ *
+ * @ingroup PM_MX27
+ */
+#ifndef __DVFS_DPTC_TABLE_MX27_H__
+#define __DVFS_DPTC_TABLE_MX27_H__
+
+/*!
+ * Default DPTC table definition
+ */
+#define NUM_OF_FREQS 1
+#define NUM_OF_WP 17
+
+static char *default_table_str = "WORKING POINT 17\n\
+\n\
+WP 0x1c\n\
+WP 0x1b\n\
+WP 0x1a\n\
+WP 0x19\n\
+WP 0x18\n\
+WP 0x17\n\
+WP 0x16\n\
+WP 0x15\n\
+WP 0x14\n\
+WP 0x13\n\
+WP 0x12\n\
+WP 0x11\n\
+WP 0x10\n\
+WP 0xf\n\
+WP 0xe\n\
+WP 0xd\n\
+WP 0xc\n\
+\n\
+DCVR 0xffe00000 0x18e2e85b 0xffe00000 0x25c4688a \n\
+DCVR 0xffe00000 0x18e2e85b 0xffe00000 0x25c4688a \n\
+DCVR 0xffe00000 0x1902e85b 0xffe00000 0x25e4688a \n\
+DCVR 0xffe00000 0x1922e85b 0xffe00000 0x25e4688a \n\
+DCVR 0xffe00000 0x1942ec5b 0xffe00000 0x2604688a \n\
+DCVR 0xffe00000 0x1942ec5b 0xffe00000 0x26646c8a \n\
+DCVR 0xffe00000 0x1962ec5b 0xffe00000 0x26c4708b \n\
+DCVR 0xffe00000 0x1962ec5b 0xffe00000 0x26e4708b \n\
+DCVR 0xffe00000 0x1982f05c 0xffe00000 0x2704748b \n\
+DCVR 0xffe00000 0x19c2f05c 0xffe00000 0x2744748b \n\
+DCVR 0xffe00000 0x1a02f45c 0xffe00000 0x2784788b \n\
+DCVR 0xffe00000 0x1a42f45c 0xffe00000 0x27c47c8b \n\
+DCVR 0xffe00000 0x1a82f85c 0xffe00000 0x2824808c \n\
+DCVR 0xffe00000 0x1aa2f85c 0xffe00000 0x2884848c \n\
+DCVR 0xffe00000 0x1ac2fc5c 0xffe00000 0x28e4888c \n\
+DCVR 0xffe00000 0x1ae2fc5c 0xffe00000 0x2924888c \n\
+DCVR 0xffe00000 0x1b23005d 0xffe00000 0x29648c8c \n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31.h b/drivers/mxc/pm/dvfs_dptc_table_mx31.h
new file mode 100644
index 000000000000..81fe80f28af8
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31.h
@@ -0,0 +1,157 @@
+/*
+ * 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
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public 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_dptc_table_mx31.h
+ *
+ * @brief MX31 dvfs & dptc table file for CKIH clock 26MHz.
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX31_H__
+#define __DVFS_DPTC_TABLE_MX31_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 3.5V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str = "WORKING POINT 17\n\
+\n\
+# mc13783 switcher SW values for each working point\n\
+# The first line is for WP of highest voltage\n\
+# The first column is for highest frequency\n\
+# SW1A SW1A DVS SW1B DVS SW1B STANDBY\n\
+WP 0x1d 0x12 0x12 0x12\n\
+WP 0x1c 0x12 0x12 0x12\n\
+WP 0x1b 0x12 0x12 0x12\n\
+WP 0x1a 0x12 0x12 0x12\n\
+WP 0x19 0x12 0x12 0x12\n\
+WP 0x18 0x12 0x12 0x12\n\
+WP 0x17 0x12 0x12 0x12\n\
+WP 0x16 0x12 0x12 0x12\n\
+WP 0x15 0x12 0x12 0x12\n\
+WP 0x14 0x12 0x12 0x12\n\
+WP 0x13 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 0 0xff871e58 0xff871e59 0x33280c 0x33280c 7\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x33280c 0x33280c 7\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e8 0xffc00000 0xe5f70da4\n\
+DCVR 0xffc00000 0x9623f8e8 0xffc00000 0xe6371da8\n\
+DCVR 0xffc00000 0x966408f0 0xffc00000 0xe6b73db0\n\
+DCVR 0xffc00000 0x96e428f4 0xffc00000 0xe7776dbc\n\
+DCVR 0xffc00000 0x976448fc 0xffc00000 0xe8379dc8\n\
+DCVR 0xffc00000 0x97e46904 0xffc00000 0xe977ddd8\n\
+DCVR 0xffc00000 0x98a48910 0xffc00000 0xeab81de8\n\
+DCVR 0xffc00000 0x9964b918 0xffc00000 0xebf86df8\n\
+DCVR 0xffc00000 0xffe4e924 0xffc00000 0xfff8ae08\n\
+DCVR 0xffc00000 0xffe5192c 0xffc00000 0xfff8fe1c\n\
+DCVR 0xffc00000 0xffe54938 0xffc00000 0xfff95e2c\n\
+DCVR 0xffc00000 0xffe57944 0xffc00000 0xfff9ae44\n\
+DCVR 0xffc00000 0xffe5b954 0xffc00000 0xfffa0e58\n\
+DCVR 0xffc00000 0xffe5e960 0xffc00000 0xfffa6e70\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h b/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h
new file mode 100644
index 000000000000..ff8b520776c0
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31_27ckih.h
@@ -0,0 +1,152 @@
+/*
+ * 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
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public 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.h
+ *
+ * @brief MX31 dvfs & dptc table file for CKIH clock 27MHz.
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX31_27CKIH_H__
+#define __DVFS_DPTC_TABLE_MX31_27CKIH_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 1.35V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str_27ckih = "WORKING POINT 17\n\
+WP 0x1d 0x12 0x12 0x12\n\
+WP 0x1c 0x12 0x12 0x12\n\
+WP 0x1b 0x12 0x12 0x12\n\
+WP 0x1a 0x12 0x12 0x12\n\
+WP 0x19 0x12 0x12 0x12\n\
+WP 0x18 0x12 0x12 0x12\n\
+WP 0x17 0x12 0x12 0x12\n\
+WP 0x16 0x12 0x12 0x12\n\
+WP 0x15 0x12 0x12 0x12\n\
+WP 0x14 0x12 0x12 0x12\n\
+WP 0x13 0x12 0x12 0x12\n\
+WP 0x12 0x12 0x12 0x12\n\
+WP 0x11 0x12 0x12 0x12\n\
+WP 0x10 0x12 0x12 0x12\n\
+WP 0xf 0x12 0x12 0x12\n\
+WP 0xe 0x12 0x12 0x12\n\
+WP 0xd 0x12 0x12 0x12\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 0 0xff871e58 0xff871e59 0xe240d 0xe240d 7\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0xe240d 0xe240d 7\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x90629890 0xffc00000 0xdd34ed20\n\
+DCVR 0xffc00000 0x90629890 0xffc00000 0xdd34ed20\n\
+DCVR 0xffc00000 0x90629894 0xffc00000 0xdd74fd24\n\
+DCVR 0xffc00000 0x90a2a894 0xffc00000 0xddb50d28\n\
+DCVR 0xffc00000 0x90e2b89c 0xffc00000 0xde352d30\n\
+DCVR 0xffc00000 0x9162d8a0 0xffc00000 0xdef55d38\n\
+DCVR 0xffc00000 0x91e2f8a8 0xffc00000 0xdfb58d44\n\
+DCVR 0xffc00000 0x926308b0 0xffc00000 0xe0b5cd54\n\
+DCVR 0xffc00000 0x92e328bc 0xffc00000 0xe1f60d64\n\
+DCVR 0xffc00000 0x93a358c0 0xffc00000 0xe3365d74\n\
+DCVR 0xffc00000 0xf66388cc 0xffc00000 0xf6768d84\n\
+DCVR 0xffc00000 0xf663b8d4 0xffc00000 0xf676dd98\n\
+DCVR 0xffc00000 0xf663e8e0 0xffc00000 0xf6773da4\n\
+DCVR 0xffc00000 0xf66418ec 0xffc00000 0xf6778dbc\n\
+DCVR 0xffc00000 0xf66458fc 0xffc00000 0xf677edd0\n\
+DCVR 0xffc00000 0xf6648908 0xffc00000 0xf6783de8\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x90400000 0xffc00000 0xdd000000\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd0d4348\n\
+DCVR 0xffc00000 0x9048a224 0xffc00000 0xdd4d4348\n\
+DCVR 0xffc00000 0x9088b228 0xffc00000 0xdd8d434c\n\
+DCVR 0xffc00000 0x90c8b228 0xffc00000 0xde0d534c\n\
+DCVR 0xffc00000 0x9148b228 0xffc00000 0xdecd5350\n\
+DCVR 0xffc00000 0x91c8c22c 0xffc00000 0xdf8d6354\n\
+DCVR 0xffc00000 0x9248d22c 0xffc00000 0xe08d7354\n\
+DCVR 0xffc00000 0x92c8d230 0xffc00000 0xe1cd8358\n\
+DCVR 0xffc00000 0x9388e234 0xffc00000 0xe30d935c\n\
+DCVR 0xffc00000 0xf648e234 0xffc00000 0xf64db364\n\
+DCVR 0xffc00000 0xf648f238 0xffc00000 0xf64dc368\n\
+DCVR 0xffc00000 0xf648f23c 0xffc00000 0xf64dd36c\n\
+DCVR 0xffc00000 0xf649023c 0xffc00000 0xf64de370\n\
+DCVR 0xffc00000 0xf649123c 0xffc00000 0xf64df374\n\
+DCVR 0xffc00000 0xf6492240 0xffc00000 0xf64e1378\n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h b/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h
new file mode 100644
index 000000000000..cb0f64c98749
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mx31_rev2.h
@@ -0,0 +1,158 @@
+/*
+ * 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
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public 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_dptc_table_mx31_rev2.h
+ *
+ * @brief MX31 dvfs & dptc table file for MX31 2.0
+ *
+ * @ingroup PM_MX31
+ */
+#ifndef __DVFS_DPTC_TABLE_MX3_REV2_H__
+#define __DVFS_DPTC_TABLE_MX3_REV2_H__
+
+#define NUM_OF_FREQS 4
+#define NUM_OF_WP 17
+
+/*!
+ * Default DPTC table definition.
+ * The table doesn't use PLL switch, because on DDR boards
+ * PLL switch is not possible due to HW issue.
+ * For SDR boards new table can be loaded.
+ *
+ * The table keeps the same voltage of 3.5V for frequencies lower than 399MHz.
+ * Theoretically we don't need DPTC for these frequencies,
+ * but we have to keep DPTC enabled for fluent DVFS switching
+ * back to high frequency.
+ */
+static char *default_table_str_rev2 = "WORKING POINT 17\n\
+\n\
+# mc13783 switcher SW values for each working point\n\
+# The first line is for WP of highest voltage\n\
+# The first column is for highest frequency\n\
+# SW1A SW1A DVS SW1B DVS SW1B STANDBY\n\
+WP 0x1d 0xc 0xc 0xc\n\
+WP 0x1c 0xc 0xc 0xc\n\
+WP 0x1b 0xc 0xc 0xc\n\
+WP 0x1a 0xc 0xc 0xc\n\
+WP 0x19 0xc 0xc 0xc\n\
+WP 0x18 0xc 0xc 0xc\n\
+WP 0x17 0xc 0xc 0xc\n\
+WP 0x16 0xc 0xc 0xc\n\
+WP 0x15 0xc 0xc 0xc\n\
+WP 0x14 0xc 0xc 0xc\n\
+WP 0x13 0xc 0xc 0xc\n\
+WP 0x12 0xc 0xc 0xc\n\
+WP 0x11 0xc 0xc 0xc\n\
+WP 0x10 0xc 0xc 0xc\n\
+WP 0xf 0xc 0xc 0xc\n\
+WP 0xe 0xc 0xc 0xc\n\
+WP 0xd 0xc 0xc 0xc\n\
+\n\
+# pll_sw_up pll_sw_down pdr_up pdr_down pll_up pll_down vscnt\n\
+# 532MHz\n\
+FREQ 0 1 0xff871e58 0xff871650 0x0033280c 0x00331c23 1\n\
+# 399MHz\n\
+FREQ 1 1 0xff871e58 0xff871e59 0x0033280c 0x0033280c 4\n\
+# 266MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x0033280c 0x0033280c 4\n\
+# 133MHz\n\
+FREQ 0 0 0xff871e58 0xff871e5b 0x0033280c 0x0033280c 4\n\
+\n\
+# 532MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e4 0xffc00000 0xe5b6fda0\n\
+DCVR 0xffc00000 0x95e3e8e8 0xffc00000 0xe5f70da4\n\
+DCVR 0xffc00000 0x9623f8e8 0xffc00000 0xe6371da8\n\
+DCVR 0xffc00000 0x966408f0 0xffc00000 0xe6b73db0\n\
+DCVR 0xffc00000 0x96e428f4 0xffc00000 0xe7776dbc\n\
+DCVR 0xffc00000 0x976448fc 0xffc00000 0xe8379dc8\n\
+DCVR 0xffc00000 0x97e46904 0xffc00000 0xe977ddd8\n\
+DCVR 0xffc00000 0x98a48910 0xffc00000 0xeab81de8\n\
+DCVR 0xffc00000 0x9964b918 0xffc00000 0xebf86df8\n\
+DCVR 0xffc00000 0xffe4e924 0xffc00000 0xfff8ae08\n\
+DCVR 0xffc00000 0xffe5192c 0xffc00000 0xfff8fe1c\n\
+DCVR 0xffc00000 0xffe54938 0xffc00000 0xfff95e2c\n\
+DCVR 0xffc00000 0xffe57944 0xffc00000 0xfff9ae44\n\
+DCVR 0xffc00000 0xffe5b954 0xffc00000 0xfffa0e58\n\
+DCVR 0xffc00000 0xffe5e960 0xffc00000 0xfffa6e70\n\
+\n\
+# 266MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+\n\
+# 133MHz\n\
+DCVR 0xffc00000 0x95c00000 0xffc00000 0xe5800000\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe58dc368\n\
+DCVR 0xffc00000 0x95c8f238 0xffc00000 0xe5cdc368\n\
+DCVR 0xffc00000 0x9609023c 0xffc00000 0xe60dc36c\n\
+DCVR 0xffc00000 0x9649023c 0xffc00000 0xe68dd36c\n\
+DCVR 0xffc00000 0x96c9023c 0xffc00000 0xe74dd370\n\
+DCVR 0xffc00000 0x97491240 0xffc00000 0xe80de374\n\
+DCVR 0xffc00000 0x97c92240 0xffc00000 0xe94df374\n\
+DCVR 0xffc00000 0x98892244 0xffc00000 0xea8e0378\n\
+DCVR 0xffc00000 0x99493248 0xffc00000 0xebce137c\n\
+DCVR 0xffc00000 0xffc93248 0xffc00000 0xffce3384\n\
+DCVR 0xffc00000 0xffc9424c 0xffc00000 0xffce4388\n\
+DCVR 0xffc00000 0xffc95250 0xffc00000 0xffce538c\n\
+DCVR 0xffc00000 0xffc96250 0xffc00000 0xffce7390\n\
+DCVR 0xffc00000 0xffc97254 0xffc00000 0xffce8394\n\
+DCVR 0xffc00000 0xffc98258 0xffc00000 0xffcea39c\n\
+";
+
+#endif
diff --git a/drivers/mxc/pm/dvfs_dptc_table_mxc91321.h b/drivers/mxc/pm/dvfs_dptc_table_mxc91321.h
new file mode 100644
index 000000000000..35b8dce893c5
--- /dev/null
+++ b/drivers/mxc/pm/dvfs_dptc_table_mxc91321.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 dptc.h
+ *
+ * @brief MXC91321 dvfs & dptc table file.
+ *
+ * @ingroup PM_MXC91321
+ */
+#ifndef __DVFS_DPTC_TABLE_MXC91321_H__
+#define __DVFS_DPTC_TABLE_MXC91321_H__
+
+/*!
+ * Default DPTC table definition
+ */
+#define NUM_OF_FREQS 1
+#define NUM_OF_WP 17
+
+static char *default_table_str = "WORKING POINT 17\n\
+\n\
+WP 0x1c\n\
+WP 0x1b\n\
+WP 0x1a\n\
+WP 0x19\n\
+WP 0x18\n\
+WP 0x17\n\
+WP 0x16\n\
+WP 0x15\n\
+WP 0x14\n\
+WP 0x13\n\
+WP 0x12\n\
+WP 0x11\n\
+WP 0x10\n\
+WP 0xf\n\
+WP 0xe\n\
+WP 0xd\n\
+WP 0xc\n\
+\n\
+DCVR 0x7fe00000 0x82E10870 0xF53DCFD0 0xCA32ED04\n\
+DCVR 0x7fe00000 0x83211874 0xF5BE0FDC 0xCAB32D10\n\
+DCVR 0x7fe00000 0x8361287C 0xF5FE2FEC 0xCAB33D1C\n\
+DCVR 0x7fe00000 0x8361388C 0xF5FE3004 0xCAF34D34\n\
+DCVR 0x7fe00000 0x83613890 0xF63E4010 0xCAF35D3C\n\
+DCVR 0x7fe00000 0x83614894 0xF63E5018 0xCB335D44\n\
+DCVR 0x7fe00000 0x83A1489C 0xF67E6024 0xCB337D4C\n\
+DCVR 0x7fe00000 0x83A158A0 0xF67E7030 0xCB337D54\n\
+DCVR 0x7fe00000 0x83A158A4 0xF6BE8038 0xCB738D60\n\
+DCVR 0x7fe00000 0x83E168A8 0xF6BEA044 0xCBB39D68\n\
+DCVR 0x7fe00000 0x83E168B0 0xF6FEB050 0xCBB3BD74\n\
+DCVR 0x7fe00000 0x83E178B8 0xF73EC05C 0xCBF3BD80\n\
+DCVR 0x7fe00000 0x842188BC 0xF77EE06C 0xCBF3DD8C\n\
+DCVR 0x7fe00000 0x842198C8 0xF77EF07C 0xCC33FD9C\n\
+DCVR 0x7fe00000 0x8421A8CC 0xF7BF108C 0xCC73FDA8\n\
+DCVR 0x7fe00000 0x8461B8D8 0xF7FF309C 0xCCB42DB8\n\
+DCVR 0x7fe00000 0x84A1C8E0 0xF83F50B0 0xCCF44DC8\n\
+";
+#endif
diff --git a/drivers/mxc/pmic/Kconfig b/drivers/mxc/pmic/Kconfig
new file mode 100644
index 000000000000..4a99b56202fa
--- /dev/null
+++ b/drivers/mxc/pmic/Kconfig
@@ -0,0 +1,62 @@
+#
+# PMIC device driver configuration
+#
+
+menu "MXC PMIC support"
+
+config MXC_SPI_PMIC_CORE
+ tristate "PMIC Protocol support (SPI interface)"
+ depends on ARCH_MXC && SPI_MXC
+ default n
+ ---help---
+ This is the PMIC core/protocol driver for the Freescale MXC application.
+ SPI should be providing the interface between the PMIC and the MCU.
+ You must select the SPI driver support to enable this option.
+
+config MXC_PMIC
+ boolean
+ default MXC_SPI_PMIC_CORE
+
+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
+
+config MXC_PMIC_MC13783
+ tristate "MC13783 Client Drivers"
+ depends on MXC_SPI_PMIC_CORE
+ default n
+ ---help---
+ This is the MXC MC13783(PMIC) client drivers support. It include
+ ADC, Audio, Battery, Connectivity, Light, Power and RTC.
+
+source "drivers/mxc/pmic/mc13783/Kconfig"
+
+config MXC_PMIC_SC55112
+ tristate "SC55112 Client Drivers"
+ depends on MXC_SPI_PMIC_CORE && ARCH_MXC91131
+ default n
+ ---help---
+ This is the MXC MC13783(PMIC) client drivers support. It include
+ ADC, Audio, Battery, Connectivity, Light, Power and RTC.
+
+config MXC_PMIC_FIXARB
+ bool "Use Arbitration Bit Workaround"
+ depends on MXC_PMIC_SC55112
+ default n
+ ---help---
+ Enable this option to include the code that will automatically
+ toggle the sc55112 arbitration bits to allow register write
+ access to the secondary processor. Note that this also requires
+ a hardware modification to the RF Deck that connects the Sphinx
+ card (with the sc55112 PMIC) to the MXC91131 EVB.
+
+
+endmenu
diff --git a/drivers/mxc/pmic/Makefile b/drivers/mxc/pmic/Makefile
new file mode 100644
index 000000000000..d937cf72d69d
--- /dev/null
+++ b/drivers/mxc/pmic/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the MXC PMIC drivers.
+#
+
+obj-y += core/
+obj-$(CONFIG_MXC_PMIC_MC13783) += mc13783/
+obj-$(CONFIG_MXC_PMIC_SC55112) += sc55112/
diff --git a/drivers/mxc/pmic/core/Makefile b/drivers/mxc/pmic/core/Makefile
new file mode 100644
index 000000000000..2fe13f764c61
--- /dev/null
+++ b/drivers/mxc/pmic/core/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the PMIC core drivers.
+#
+obj-$(CONFIG_MXC_SPI_PMIC_CORE) += pmic_core_spi_mod.o
+obj-$(CONFIG_MXC_PMIC_CHARDEV) += pmic-dev.o
+
+pmic_core_spi_mod-objs := pmic_external.o pmic_event.o pmic_core_spi.o
+
+ifeq ($(CONFIG_MXC_PMIC_MC13783),y)
+pmic_core_spi_mod-objs += mc13783.o
+endif
+
+ifeq ($(CONFIG_MXC_PMIC_SC55112),y)
+pmic_core_spi_mod-objs += sc55112.o
+endif
diff --git a/drivers/mxc/pmic/core/mc13783.c b/drivers/mxc/pmic/core/mc13783.c
new file mode 100644
index 000000000000..0c81f0d5dc56
--- /dev/null
+++ b/drivers/mxc/pmic/core/mc13783.c
@@ -0,0 +1,368 @@
+/*
+ * 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 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/spi/spi.h>
+
+#include <asm/uaccess.h>
+#include <asm/arch/gpio.h>
+
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_status.h>
+#include "pmic_config.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;
+static 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;
+}
+
+/*!
+ * 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/pmic-dev.c b/drivers/mxc/pmic/core/pmic-dev.c
new file mode 100644
index 000000000000..ffdeb68ce2eb
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic-dev.c
@@ -0,0 +1,318 @@
+/*
+ * 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 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 <asm/uaccess.h>
+#include <asm/arch/pmic_external.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 class_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 =
+ class_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:
+ class_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)
+{
+ class_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..2456600112fa
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic.h
@@ -0,0 +1,132 @@
+/*
+ * 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 __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
+
+#endif /* __PMIC_H__ */
diff --git a/drivers/mxc/pmic/core/pmic_config.h b/drivers/mxc/pmic/core/pmic_config.h
new file mode 100644
index 000000000000..0dae58db044b
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_config.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
+ */
+#ifndef __PMIC_CONFIG_H__
+#define __PMIC_CONFIG_H__
+
+ /*!
+ * @file pmic_config.h
+ * @brief This file contains configuration define used by PMIC Client drivers.
+ *
+ * @ingroup PMIC_CORE
+ */
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h> /* printk() */
+#include <linux/module.h> /* modules */
+#include <linux/init.h> /* module_{init,exit}() */
+#include <linux/slab.h> /* kmalloc()/kfree() */
+#include <linux/stddef.h>
+
+#include <asm/uaccess.h> /* copy_{from,to}_user() */
+#include <asm/arch/pmic_status.h>
+#include <asm/arch/pmic_external.h>
+
+/*
+ * 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 /* __PMIC_CONFIG_H__ */
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..8b28b49ed3cd
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_core_spi.c
@@ -0,0 +1,326 @@
+/*
+ * 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 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 <asm/uaccess.h>
+#include <asm/arch/gpio.h>
+
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_status.h>
+#include "pmic.h"
+
+/*
+ * Global variables
+ */
+static pmic_version_t mxc_pmic_version;
+unsigned int active_events[MAX_ACTIVE_EVENTS];
+
+/*
+ * Static functions
+ */
+static void pmic_pdev_register(void);
+static void pmic_pdev_unregister(void);
+void pmic_bh_handler(struct work_struct *work);
+
+/*
+ * 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,
+};
+
+/*
+ * External functions
+ */
+extern void pmic_event_list_init(void);
+extern void pmic_event_callback(type_event event);
+extern void gpio_pmic_active(void);
+
+/*!
+ * Bottom half handler of PMIC event handling.
+ */
+DECLARE_WORK(pmic_ws, pmic_bh_handler);
+
+/*!
+ * 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);
+}
+
+/*!
+ * 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 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.
+ */
+static irqreturn_t pmic_irq_handler(int irq, void *dev_id)
+{
+ /* prepare a task */
+ schedule_work(&pmic_ws);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * 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);
+
+ for (loop = 0; loop < count; loop++) {
+ pmic_event_callback(active_events[loop]);
+ }
+
+ return;
+}
+
+/*!
+ * 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);
+
+/*!
+ * 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;
+}
+
+/*!
+ * 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;
+
+ 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);
+ }
+
+ /* Initialize the PMIC parameters */
+ ret = pmic_init_registers();
+ if (ret != PMIC_SUCCESS) {
+ return PMIC_ERROR;
+ }
+
+ /* 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) {
+ dev_err((struct device *)spi, "gpio1: irq%d error.", spi->irq);
+ return ret;
+ }
+
+ 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)
+{
+ 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)
+{
+ pr_debug("Registering the PMIC Protocol Driver\n");
+ 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(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..e224b4c6c269
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_event.c
@@ -0,0 +1,237 @@
+/*
+ * 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 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 <asm/uaccess.h>
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/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..9e092eb0907c
--- /dev/null
+++ b/drivers/mxc/pmic/core/pmic_external.c
@@ -0,0 +1,105 @@
+/*
+ * 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 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 <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_status.h>
+
+#include "pmic_config.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..5f72f0d43d2a
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_adc.c
@@ -0,0 +1,1504 @@
+/*
+ * 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_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 <asm/arch/pmic_adc.h>
+#include <asm/arch/pmic_power.h>
+
+#include "../core/pmic_config.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;
+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
+};
+
+/*!
+ * 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 */
+ suspend_flag = 0;
+ 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));
+
+ 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");
+ adc_0_reg = 0x001c00 | (ADC_BIS * use_bis);
+ pmic_event_unmask(EVENT_TSI);
+ CHECK_ERROR(pmic_write_reg
+ (REG_ADC_0, adc_0_reg, PMIC_ALL_BITS));
+ adc_1_reg = 0x200001 | (ADC_BIS * adc_ts);
+ 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;
+ }
+ use_bis = mc13783_adc_request();
+ 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);
+ 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)
+{
+ mc13783_adc_read_ts(touch_sample, wait);
+ pmic_adc_filter(touch_sample);
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * 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;
+ mc13783_adc_convert(&param);
+ /* 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(void)
+{
+ int adc_index = -1;
+ if (((adc_dev[0] == ADC_USED) && (adc_dev[1] == ADC_USED))) {
+ /* all ADC is used wait... */
+ wait_event(queue_adc_busy, 0);
+ } else if (adc_dev[0] == ADC_FREE) {
+ adc_dev[0] = ADC_USED;
+ adc_index = 0;
+ } else if (adc_dev[1] == ADC_FREE) {
+ 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();
+ 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 class_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 = 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;
+ }
+
+ pr_debug(KERN_INFO "PMIC ADC successfully probed\n");
+ return ret;
+
+ err_out4:
+ class_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_deinit();
+ class_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..c2a282c829ca
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_adc_defs.h
@@ -0,0 +1,321 @@
+/*
+ * 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_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(void);
+
+/*!
+ * 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..f73ffb47cbf7
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_audio.c
@@ -0,0 +1,5819 @@
+/*
+ * 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_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 <asm/arch/pmic_audio.h> /* For PMIC Audio driver interface. */
+#include <asm/arch/pmic_adc.h> /* For PMIC ADC driver interface. */
+
+#include "../core/pmic_config.h"
+
+/*
+ * 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_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);
+ } 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;
+
+ /* 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)) {
+ /* 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);
+ /* 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 {
+ if ((gain >= OUTPGA_GAIN_MINUS_33DB)
+ && (gain <= OUTPGA_GAIN_PLUS_6DB)) {
+ reg_gain = gain + 2;
+ } else {
+ reg_gain = gain;
+ }
+ 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;
+}
+
+/*@}*/
+
+/**************************************************************************
+ * 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..66f578c8a8c6
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_battery.c
@@ -0,0 +1,938 @@
+/*
+ * 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.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 <asm/arch/pmic_battery.h>
+#include <asm/arch/pmic_adc.h>
+
+#include "pmic_battery_defs.h"
+#include "../core/pmic_config.h"
+
+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);
+
+/*!
+ * 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 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_WLOWI;
+ 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(event, bat_callback));
+ } else {
+ CHECK_ERROR(pmic_event_unsubscribe(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;
+ 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_CURRENT:
+ CHECK_ERROR(pmic_batt_get_charge_current(&c_current));
+ if (copy_to_user((unsigned char *)arg, &c_current,
+ sizeof(unsigned char *))) {
+ 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;
+ }
+ }
+ return 0;
+}
+
+/*!
+ * 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;
+ }
+ }
+ 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)
+{
+ class_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 class_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 = 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;
+ }
+
+ 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..959d17240312
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_convity.c
@@ -0,0 +1,2483 @@
+/*
+ * 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_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 <asm/arch/pmic_convity.h> /* For PMIC Connectivity driver interface. */
+#include <asm/arch/pmic_adc.h> /* For PMIC ADC driver interface. */
+
+#include "../core/pmic_config.h"
+
+/*
+ * 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..60999028b753
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_light.c
@@ -0,0 +1,2768 @@
+/*
+ * 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.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 <asm/arch/pmic_light.h>
+#include "../core/pmic_config.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)
+{
+ class_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 class_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 = 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:
+ class_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..11f96bb7addb
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_power.c
@@ -0,0 +1,3079 @@
+/*
+ * 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.c
+ * @brief This is the main file of PMIC(mc13783) Power driver.
+ *
+ * @ingroup PMIC_POWER
+ */
+
+/*
+ * Includes
+ */
+
+#include <linux/platform_device.h>
+#include <asm/ioctl.h>
+#include <asm/arch/pmic_power.h>
+
+#include "../core/pmic_config.h"
+#include "pmic_power_defs.h"
+
+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.
+ */
+PMIC_STATUS 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);
+
+ CHECK_ERROR(pmic_write_reg(REG_POWER_CONTROL_0, value, mask));
+
+ return PMIC_SUCCESS;
+}
+
+/*!
+ * 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);
+}
+
+/*
+ * Init and Exit
+ */
+
+static int pmic_power_probe(struct platform_device *pdev)
+{
+ printk(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");
+ return platform_driver_register(&pmic_power_driver_ldm);
+}
+static void __exit pmic_power_exit(void)
+{
+ platform_driver_unregister(&pmic_power_driver_ldm);
+ pr_debug("PMIC Power driver successfully unloaded\n");
+}
+
+/*
+ * Module entry points
+ */
+
+subsys_initcall(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..94ba4efacf60
--- /dev/null
+++ b/drivers/mxc/pmic/mc13783/pmic_rtc.c
@@ -0,0 +1,549 @@
+/*
+ * 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_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 <asm/arch/pmic_rtc.h>
+
+#include "../core/pmic_config.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;
+
+ down_interruptible(&mutex);
+ 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)
+{
+ class_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 class_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 = 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/security/Kconfig b/drivers/mxc/security/Kconfig
new file mode 100644
index 000000000000..f41a84ac84fd
--- /dev/null
+++ b/drivers/mxc/security/Kconfig
@@ -0,0 +1,101 @@
+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 SCC_DEBUG
+ bool "MXC SCC Module debugging"
+ depends on MXC_SECURITY_SCC
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables HAC module debugging.
+
+config MXC_SECURITY_SCC2
+ tristate "MXC SCC2 Driver"
+ depends on ARCH_MXC92323
+ 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 SCC2 Module debugging"
+ depends on MXC_SECURITY_SCC2
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables HAC 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_SECURITY_HAC
+ tristate "MXC HAC Driver"
+ depends on ARCH_MXC
+ depends on ARCH_MXC91131
+ default n
+ select MXC_SECURITY_CORE
+ ---help---
+ This module contains the core API's for accessing the HAC module.
+ If you are unsure about this, say N here.
+
+config MXC_HAC_TEST_DEBUG
+ bool "MXC HAC Module debugging"
+ depends on MXC_SECURITY_HAC
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables HAC module debugging.
+
+config MXC_SECURITY_RTIC
+ tristate "MXC RTIC Driver"
+ depends on ARCH_MXC
+ depends on !ARCH_MX21
+ default n
+ select MXC_SECURITY_CORE
+ ---help---
+ This module contains the core API's for accessing the RTIC module.
+ If you are unsure about this, say N here.
+
+config MXC_RTIC_TEST_DEBUG
+ bool "MXC RTIC module debugging"
+ depends on MXC_SECURITY_RTIC
+ default n
+ ---help---
+ This is an option for use by developers; most people should
+ say N here. This enables RTIC module debugging.
+
+config MXC_SECURITY_CORE
+ tristate
+
+if (ARCH_MXC91231 || ARCH_MXC91321 || ARCH_MX27 || ARCH_MXC92323) && MXC_SECURITY_RNG=n && MXC_SECURITY_HAC=n
+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..73e21cf76f9a
--- /dev/null
+++ b/drivers/mxc/security/Makefile
@@ -0,0 +1,21 @@
+# Makefile for the Linux MXC Security API
+ifeq ($( SCC_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+ifeq ($(MXC_HAC_TEST_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+ifeq ($(MXC_RTIC_TEST_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_RTIC) += mxc_rtic.o
+obj-$(CONFIG_MXC_SECURITY_HAC) += mxc_hacc.o
+obj-$(CONFIG_MXC_SECURITY_RNG) += rng/
+obj-$(CONFIG_MXC_SECURITY_CORE) += mxc_sec_mod.o
+obj-$(CONFIG_MXC_SAHARA) += sahara2/
diff --git a/drivers/mxc/security/mxc_hacc.c b/drivers/mxc/security/mxc_hacc.c
new file mode 100644
index 000000000000..46829cd791e1
--- /dev/null
+++ b/drivers/mxc/security/mxc_hacc.c
@@ -0,0 +1,523 @@
+/*
+ * 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_hacc.c
+ *
+ * @brief APIs for HAC Module.
+ *
+ * This file provides the APIs for accessing Hash Acceleration (HAC)
+ * module. HAC module accelerates the creation of a SHA-1 hash over
+ * selected memory spaces. The SHA-1 algorithm is a one-way hash
+ * algorithm that creates 160 bit hash of any length input.
+ *
+ * @ingroup MXC_Security
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#ifdef CONFIG_MXC_HAC_TEST_DEBUG
+#include <linux/module.h>
+#endif /* CONFIG_MXC_HAC_TEST_DEBUG */
+#include "mxc_hacc.h"
+
+/*!
+ * This variable indicates whether HAC module is in suspend or in resume
+ * mode.
+ */
+static unsigned short hac_suspend_state = 0;
+
+/*!
+ * This API configures the start address and block length of the data that
+ * needs to be hashed. Start address indicates starting location from where
+ * the data in the flash memory is to be hashed. The number of blocks that
+ * needs to be hashed is loaded in the block count register. This API does
+ * the starting of Hashing process or continue with Hashing of next block of
+ * data configured in the START_ADDR, BLOCK_COUNT register depending on the
+ * hash parameter passed.
+ *
+ * @param start_address Starting address of the flash memory to be hashed.
+ * user has to pass physical address here.
+ * @param blk_len Number of blocks to be hashed.
+ * @param option Mode of operation like Start or Continue hashing.\n
+ * Following parameters are passed:
+ * HAC_START : Starts the Hashing process.
+ * HAC_LAST_START : Starts the Hashing process with
+ * last block of data.
+ * HAC_CONTINUE : Continue the Hashing process.
+ * HAC_LAST : Continue the Hashing process with
+ * last block of data.
+ *
+ *
+ * @return HAC_SUCCESS Successfully hashed the data.\n
+ * HAC_FAILURE Error in the parameters passed.
+ * HAC_BUSY HAC module is busy in Hashing process.
+ */
+hac_ret hac_hash_data(ulong start_address, ulong blk_len, hac_hash option)
+{
+ struct clk *clk;
+ ulong hac_start, hac_blk_cnt, hac_ctl;
+ hac_ret ret_val = HAC_SUCCESS;
+
+ clk = clk_get(NULL, "hac_clk");
+ clk_enable(clk);
+ hac_start = __raw_readl(HAC_START_ADDR);
+ hac_blk_cnt = __raw_readl(HAC_BLK_CNT);
+ hac_ctl = __raw_readl(HAC_CTL);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return -EPERM;
+ }
+ pr_debug("Function %s. HAC Module: Start address: 0x%08lX, "
+ "block length: 0x%08lX, hash option: 0x%08X\n",
+ __FUNCTION__, start_address, blk_len, option);
+ /* Validating the parameters. Checking for start address to be in
+ 512 bit boundary(64 byte) and block count value must not to be
+ zero. */
+ if ((!start_address) || (blk_len > HAC_MAX_BLOCK_LENGTH) ||
+ (blk_len == 0) || (!((start_address % 64) == 0))) {
+ pr_debug("HAC Module: Invalid parameters passed. \n");
+ return HAC_FAILURE;
+ }
+ if ((hac_ctl & HAC_CTL_BUSY) == 0) {
+ hac_start = start_address;
+ __raw_writel(hac_start, HAC_START_ADDR);
+ hac_blk_cnt = blk_len;
+ __raw_writel(hac_blk_cnt, HAC_BLK_CNT);
+ pr_debug("HAC Module: Hashing start address 0x%08lX\n ",
+ start_address);
+ pr_debug("HAC Module: Hashing blk length 0x%08lX\n ", blk_len);
+ } else {
+ pr_debug("HAC Module: HAC module is busy in Hashing "
+ "process.\n");
+ return HAC_HASH_BUSY;
+ }
+
+ switch (option) {
+ case HAC_START:
+ /*
+ * HAC_START will starts the Hashing of data in the memory.
+ * Before starting the Hashing process, it checks for 'STOP'
+ * bit, 'DONE' bit and 'ERROR' bit is set in the HAC Control
+ * register. If 'STOP' bit is set, it clears the 'STOP' bit in
+ * HAC Control register. If 'DONE' bit and 'ERROR' bit are
+ * set, they are cleared.
+ */
+ pr_debug("HAC Module: Starts the hashing process \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ pr_debug("HAC Module: STOP bit is set while"
+ "starting the Hashing\n");
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit and 'ERROR' bit is been set.
+ If they are set write to clear those bits */
+ if (((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) ||
+ ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR)) {
+ pr_debug("HAC Module: DONE and ERROR bit is set"
+ "while starting the Hashing\n");
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ hac_ctl |= HAC_CTL_ERROR;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ hac_ctl |= HAC_CTL_START;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_START_LAST:
+ /*
+ * HAC_START_LAST will starts the Hashing of last block of data
+ * in the memory. Since this is last block of data in the
+ * memory 'PAD' bit in HAC control register is set to add
+ * appropriate padding to the end of the data structure.
+ * Before starting the Hashing process, it checks for 'STOP'
+ * bit, 'DONE' bit and 'ERROR' bit is set in the HAC Control
+ * register. If 'STOP' bit is set, it clears the 'STOP' bit in
+ * HAC Control register. If 'DONE' bit and 'ERROR' bit are
+ * set, they are cleared.
+ */
+ pr_debug("HAC Module: Starts with last block"
+ "the hashing process \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ pr_debug("HAC Module: STOP bit is set while"
+ "starting the Hashing\n");
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit and 'ERROR' bit is been set.
+ If they are set write to clear those bits */
+ if (((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) ||
+ ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR)) {
+ pr_debug(" HAC Module: DONE and ERROR bit is set"
+ "while starting the Hashing\n");
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ hac_ctl |= HAC_CTL_ERROR;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ hac_ctl |= HAC_CTL_START;
+ __raw_writel(hac_ctl, HAC_CTL);
+ /* Hash for the last block by padding it. */
+ pr_debug("HAC Module: Setting the PAD bit while start"
+ "Hashing the last block\n");
+ hac_ctl |= HAC_CTL_PAD;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_CONTINUE:
+ /*
+ * HAC_CONTINUE will continue the Hashing of data in the memory.
+ * This will continue the hashing processing by taking into
+ * consideration of the previous hash result and continues
+ * further hashing of the new data block. Before continue the
+ * Hashing process, it checks for 'STOP' bit, 'DONE' bit and
+ * 'ERROR' bit is set in the HAC Control register. If 'STOP'
+ * bit is set, it clears the 'STOP' bit in Control register.
+ * If 'DONE' bit is set, it clears the 'DONE' bit in control
+ * register. If 'ERROR' bit is set, then error message is
+ * indicated to the user.
+ */
+ pr_debug("HAC Module: Continue hashing process. \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit is been set. If it is set write
+ one to clear the bit */
+ if ((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) {
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if 'ERROR' bit is been set. If it is set resturn
+ return back indicating error in Hashing porcess. */
+ if ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR) {
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_CONTINUE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_LAST:
+ /*
+ * HAC_LAST will continue the Hashing of last block of data
+ * in the memory. Since this is last block of data in the
+ * memory 'PAD' bit in HAC control register is set to add
+ * appropriate padding to the end of the data structure.
+ * This will continue the hashing processing by taking into
+ * consideration of the previous hash result and continues
+ * further hashing of the new data block. Before continue the
+ * Hashing process, it checks for 'STOP' bit, 'DONE' bit and
+ * 'ERROR' bit is set in the HAC Control register. If 'STOP'
+ * bit is set, it clears the 'STOP' bit in Control register.
+ * If 'DONE' bit is set, it clears the 'DONE' bit in control
+ * register. If 'ERROR' bit is set, then error message is
+ * indicated to the user.
+ */
+ pr_debug("HAC Module: Last block to hash. \n");
+ /* Checking if the Stop bit is been set. */
+ if ((hac_ctl & HAC_CTL_STOP) == HAC_CTL_STOP) {
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if the 'DONE' bit is been set. If it is set write
+ one to clear the bit */
+ if ((hac_ctl & HAC_CTL_DONE) == HAC_CTL_DONE) {
+ hac_ctl |= HAC_CTL_DONE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ }
+ /* Checking if 'ERROR' bit is been set. If it is set resturn
+ return back indicating error in Hashing porcess. */
+ if ((hac_ctl & HAC_CTL_ERROR) == HAC_CTL_ERROR) {
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_CONTINUE;
+ __raw_writel(hac_ctl, HAC_CTL);
+ /* Continuing the hash for the last block by padding it. */
+ hac_ctl |= HAC_CTL_PAD;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ default:
+ ret_val = HAC_FAILURE;
+ /* NOT RESPONDING */
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API returns the status of the Hashing.
+ *
+ * @return HAC_BUSY : Indicated HAC Module is busy with Hashing.\n
+ * HAC_DONE : Indicates Hashing of data is done.\n
+ * HAC_ERR : Indicates error has occurred during Hashing.\n
+ * HAC_UNKNOWN: Hashing status unknown. This may be when the
+ * hashing process has not been initiated atleast
+ * once or 'ERROR' bit or 'DONE' bits were reset
+ * after the hashing process was completed.
+ */
+hac_hash_status hac_hashing_status(void)
+{
+ ulong hac_ctl;
+ hac_ctl = __raw_readl(HAC_CTL);
+ if ((hac_ctl & HAC_CTL_BUSY) != 0) {
+ pr_debug("HAC Module: Hash module is in busy state \n");
+ return HAC_BUSY;
+ } else if ((hac_ctl & HAC_CTL_DONE) != 0) {
+ /* Clearing the done bit of the control register */
+ pr_debug("HAC Module: Hashing of data is done \n");
+ return HAC_DONE;
+ } else if ((hac_ctl & HAC_CTL_ERROR) != 0) {
+ /* Clearing the error bit of the control register */
+ pr_debug("HAC Module: Error has occurred during hashing \n");
+ return HAC_ERR;
+ } else {
+ return HAC_UNKNOWN;
+ }
+}
+
+/*!
+ * This API returns the status of the Hash module.
+ *
+ * @return Value of the Hashing control register.
+ */
+ulong hac_get_status(void)
+{
+ ulong hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: Hashing status register value 0x%08lX\n ",
+ hac_ctl);
+ return hac_ctl;
+}
+
+/*!
+ * This API stops the Hashing of data when the Hashing is in progress.
+ */
+hac_ret hac_stop(void)
+{
+ ulong hac_ctl;
+ hac_ctl = __raw_readl(HAC_CTL);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ pr_debug("HAC Module: Stop hashing process. \n");
+ hac_ctl |= HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This API reads 160 bit hash result from Hash result register. The data is
+ * copied to the memory pointed by the input pointer.
+ *
+ * @param hash_result_reg structure Pointer where the hash result is
+ * copied.
+ */
+hac_ret hac_hash_result(hac_hash_rlt * hash_result_reg)
+{
+ ulong hac_hsh4, hac_hsh3, hac_hsh2, hac_hsh1, hac_hsh0;
+ struct clk *clk;
+
+ clk = clk_get(NULL, "hac_clk");
+ hac_hsh4 = __raw_readl(HAC_HSH4);
+ hac_hsh3 = __raw_readl(HAC_HSH3);
+ hac_hsh2 = __raw_readl(HAC_HSH2);
+ hac_hsh1 = __raw_readl(HAC_HSH1);
+ hac_hsh0 = __raw_readl(HAC_HSH0);
+ clk_disable(clk);
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC Module: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ pr_debug("HAC Module: Read hash result \n");
+ hash_result_reg->hash_result[0] = hac_hsh4;
+ hash_result_reg->hash_result[1] = hac_hsh3;
+ hash_result_reg->hash_result[2] = hac_hsh2;
+ hash_result_reg->hash_result[3] = hac_hsh1;
+ hash_result_reg->hash_result[4] = hac_hsh0;
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This API will initiates software reset of the entire HAC module. It resets
+ * all state machine to their default values. All status bits (BUSY/ERROR/DONE)
+ * and any pending interrupts are cleared.
+ *
+ * @return HAC_SUCCESS Successfully in doing software reset.\n
+ * HAC_FAILURE Error in doing software reset.
+ */
+hac_ret hac_swrst(void)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Software reset function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ hac_ctl |= HAC_CTL_SWRST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ return hac_ret;
+}
+
+/*!
+ * This API configures the burst mode of the HAC. When Burst mode set in HAC
+ * Control register then ARM9 is configured for a 16-WORD burst, while Burst
+ * mode is cleared then ARM9 is configured for a incremental burst.
+ *
+ * @param burst_mode Configures burst mode operations.
+ *
+ * @return HAC_SUCCESS Successfully in configuring burst mode.\n
+ * HAC_FAILURE Error in configuring burst mode.
+ */
+hac_ret hac_burst_mode(hac_burst_mode_config burst_mode)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Burst Mode function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ switch (burst_mode) {
+ case HAC_INR_BURST:
+ hac_ctl |= HAC_CTL_BURST_MODE;
+ break;
+
+ case HAC_16WORD_BURST:
+ hac_ctl &= ~HAC_CTL_BURST_MODE;
+ break;
+
+ default:
+ hac_ret = HAC_FAILURE;
+ break;
+ }
+ return hac_ret;
+}
+
+/*!
+ * This API configures HAC burst read nature.
+ *
+ * @param burst_read Configures burst read.
+ *
+ * @return HAC_SUCCESS Successfully in configuring burst read.\n
+ * HAC_FAILURE Error in configuring burst read.
+ */
+hac_ret hac_burst_read(hac_burst_read_config burst_read)
+{
+ ulong hac_ctl;
+ ulong hac_ret = HAC_SUCCESS;
+ hac_ctl = __raw_readl(HAC_CTL);
+ pr_debug("HAC Module: HAC Burst Read function. \n");
+ if (hac_suspend_state == 1) {
+ pr_debug("HAC MODULE: HAC Module is in suspend mode.\n");
+ return HAC_FAILURE;
+ }
+ switch (burst_read) {
+ case HAC_16WORD_BURST_READ:
+ __raw_writel(HAC_CTL_16WORD_BURST, HAC_CTL);
+ break;
+
+ case HAC_8WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_8WORD_BURST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_4WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_4WORD_BURST;
+ __raw_writel(hac_ctl, HAC_CTL);
+ break;
+
+ case HAC_NO_WORD_BURST_READ:
+ hac_ctl &= ~HAC_CTL_NO_BURST_READ;
+ hac_ctl |= HAC_CTL_NO_BURST_READ;
+ break;
+
+ default:
+ hac_ret = HAC_FAILURE;
+ break;
+ }
+ return hac_ret;
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function is called to put the HAC 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 HAC
+ * to suspend.
+ * @param state the power state the device is entering.
+ *
+ * @return The function always returns HAC_SUCCESS.
+ */
+hac_ret hac_suspend(struct platform_device * pdev, pm_message_t state)
+{
+ ulong hac_ctl;
+ struct clk *clk;
+
+ hac_ctl = __raw_readl(HAC_CTL);
+ clk = clk_get(NULL, "hac_clk");
+ hac_suspend_state = 1;
+
+ pr_debug("HAC Module: In suspend power down.\n");
+
+ /* Enable stop bits in HAC Control Register. */
+ hac_ctl |= HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+ clk_disable(clk);
+
+ return HAC_SUCCESS;
+}
+
+/*!
+ * This function is called to bring the HAC 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 HAC
+ * to resume.
+ *
+ * @return The function always returns HAC_SUCCESS.
+ */
+hac_ret hac_resume(struct platform_device * pdev)
+{
+ ulong hac_ctl;
+ struct clk *clk;
+
+ clk = clk_get(NULL, "hac_clk");
+ clk_enable(clk);
+ hac_ctl = __raw_readl(HAC_CTL);
+
+ pr_debug("HAC Module: Resume power on.\n");
+ /* Disable stop bit in HAC Control register. */
+ hac_ctl &= ~HAC_CTL_STOP;
+ __raw_writel(hac_ctl, HAC_CTL);
+
+ hac_suspend_state = 0;
+
+ return HAC_SUCCESS;
+}
+#else
+#define mxc_hac_suspend NULL
+#define mxc_hac_resume NULL
+#endif /* CONFIG_PM */
diff --git a/drivers/mxc/security/mxc_hacc.h b/drivers/mxc/security/mxc_hacc.h
new file mode 100644
index 000000000000..ecbdcd588d49
--- /dev/null
+++ b/drivers/mxc/security/mxc_hacc.h
@@ -0,0 +1,145 @@
+/*
+ * 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_hacc.h
+ *
+ * @brief The header file for Hash Acceleration (HAC) module.
+ * This file contains all register defines and bit definition of HAC module.
+ *
+ * @ingroup MXC_Security
+ */
+
+#ifndef __MXC_HACC_H__
+#define __MXC_HACC_H__
+
+#include <asm/arch/mxc_security_api.h>
+#include <asm/hardware.h>
+#include <asm/errno.h>
+
+/*
+ * HAC Control register
+ */
+#define HAC_CTL IO_ADDRESS(HAC_BASE_ADDR + 0x00)
+
+/*
+ * HAC Start Address Register
+ */
+#define HAC_START_ADDR IO_ADDRESS(HAC_BASE_ADDR + 0x04)
+
+/*
+ * HAC Block Count Register
+ */
+#define HAC_BLK_CNT IO_ADDRESS(HAC_BASE_ADDR + 0x08)
+
+/*
+ * HAC Hash Register 4 Register
+ */
+#define HAC_HSH4 IO_ADDRESS(HAC_BASE_ADDR + 0x0C)
+
+/*
+ * HAC Hash Register 3 Register
+ */
+#define HAC_HSH3 IO_ADDRESS(HAC_BASE_ADDR + 0x10)
+
+/*
+ * HAC Hash Register 2 Register
+ */
+#define HAC_HSH2 IO_ADDRESS(HAC_BASE_ADDR + 0x14)
+
+/*
+ * HAC Hash Register 1 Register
+ */
+#define HAC_HSH1 IO_ADDRESS(HAC_BASE_ADDR + 0x18)
+
+/*
+ * HAC Hash Register 0 Register
+ */
+#define HAC_HSH0 IO_ADDRESS(HAC_BASE_ADDR + 0x1C)
+
+/*!
+ * HAC Hash Done status
+ */
+#define HAC_CTL_DONE 0x01
+
+/*!
+ * HAC Hash Error Status
+ */
+#define HAC_CTL_ERROR 0x02
+
+/*!
+ * HAC Hash Accelerator Module Busy Status
+ */
+#define HAC_CTL_BUSY 0x04
+
+/*!
+ * HAC HAC Interrupt Mask Bit.
+ */
+#define HAC_CTL_IMSK 0x08
+
+/*!
+ * HAC Command to Stop hash processing
+ */
+#define HAC_CTL_STOP 0x10
+
+/*!
+ * HAC Command for software reset
+ */
+#define HAC_CTL_SWRST 0x20
+
+/*!
+ * HAC Command to start hash processing
+ */
+#define HAC_CTL_START 0x40
+
+/*!
+ * HAC Command to continue hash processing
+ */
+#define HAC_CTL_CONTINUE 0x80
+
+/*!
+ * HAC Command to do padding
+ */
+#define HAC_CTL_PAD 0x100
+
+/*!
+ * HAC Command not to do burst read
+ */
+#define HAC_CTL_NO_BURST_READ 0x600
+
+/*!
+ * HAC Command to do 4 word Burst read
+ */
+#define HAC_CTL_4WORD_BURST 0x100
+
+/*!
+ * HAC Command to do 8 word Burst read
+ */
+#define HAC_CTL_8WORD_BURST 0x400
+
+/*!
+ * HAC Command to do 16 word Burst read
+ */
+#define HAC_CTL_16WORD_BURST 0x000
+
+/*!
+ * HAC Command to configure the burst mode
+ */
+#define HAC_CTL_BURST_MODE 0x800
+
+/*!
+ * Maximum block length that can be configured to the HAC module.
+ */
+#define HAC_MAX_BLOCK_LENGTH 0x7FFFFF
+
+#endif /* __MXC_HACC_H__ */
diff --git a/drivers/mxc/security/mxc_rtic.c b/drivers/mxc/security/mxc_rtic.c
new file mode 100644
index 000000000000..9dae7dec421e
--- /dev/null
+++ b/drivers/mxc/security/mxc_rtic.c
@@ -0,0 +1,838 @@
+/*
+ * 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_rtic.c
+ *
+ * @brief APIs for RTIC Module.
+ *
+ * \b
+ * This file provides the APIs for accessing Run-Time Integrity Checker (RTIC)
+ * module. RTIC module accelerates the creation of a SHA-1 hash over
+ * selected memory spaces. The RTIC has the ability to verify the memory
+ * contents during system boot and during run-time execution.
+ *
+ * @ingroup MXC_Security
+ */
+
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#ifdef CONFIG_MXC_RTIC_TEST_DEBUG
+#include <linux/module.h>
+#endif /* CONFIG_MXC_RTIC_TEST_DEBUG */
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include "mxc_rtic.h"
+
+/*!
+ * The following api is used to enable the RTIC IP CLK and RTIC HCLK.
+ * Before start using other APIs make sure that init is called.
+ *
+ * @param void
+ *
+ * return void
+ */
+
+void rtic_init(void)
+{
+ struct clk *clk;
+
+ clk = clk_get(NULL, "rtic_clk");
+ clk_enable(clk);
+}
+
+/*!
+ * This API configures the memory block (A, B, C, D) into
+ * RUN_TIME Mode for Hashing of data in memory. RTIC does not support
+ * enabling multiple memory blocks that aren't grouped together
+ * (i.e. enabling only memory blocks A & C without memory B enabled).
+ *
+ * @param mode RTIC mode of operation (ONE_TIME or RUN_TIME).
+ * @param mem_blk Memory block to be hashed.
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+rtic_ret rtic_configure_mode(rtic_mode mode, rtic_memblk mem_blk)
+{
+ ulong rtic_ctrl, rtic_sts, rtic_cmd;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ rtic_cmd = __raw_readl(RTIC_COMMAND);
+ /*
+ * RTIC does not support enabling multiple memory blocks that aren't
+ * grouped together(i.e. enabling only memory blocks A & C without
+ * memory B enabled).
+ */
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (mode) {
+ case RTIC_ONE_TIME:
+ break;
+
+ case RTIC_RUN_TIME:
+#ifndef CONFIG_ARCH_MX27
+ /* Check RUN Time Disable bit before enabling RUN Time memory
+ blocks. */
+ if ((rtic_cmd & RTIC_RUN_TIME_DISABLE) == RTIC_RUN_TIME_DISABLE) {
+ rtic_cmd &= ~RTIC_RUN_TIME_DISABLE;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ }
+#endif /* CONFIG_ARCH_MX27 */
+ switch (mem_blk) {
+ case RTIC_A1:
+ pr_debug("RTIC Module:Memory Block A is enabled for"
+ "Run_Time Hashing.\n");
+ rtic_ctrl &= ~RTIC_CTL_HASHONCE_MEMA_BLK_EN;
+ rtic_ctrl |= RTIC_CTL_RUNTIME_MEMA_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_B1:
+ pr_debug("RTIC Module:Memory Block B is "
+ "enabled for Run_Time Hashing.\n");
+ rtic_ctrl &= ~RTIC_CTL_HASHONCE_MEMB_BLK_EN;
+ rtic_ctrl |= RTIC_CTL_RUNTIME_MEMB_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_C1:
+ pr_debug("RTIC Module:Memory Block C is "
+ "enabled for Run_Time Hashing.\n");
+ rtic_ctrl &= ~RTIC_CTL_HASHONCE_MEMC_BLK_EN;
+ rtic_ctrl |= RTIC_CTL_RUNTIME_MEMC_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_D1:
+ pr_debug("RTIC Module:Memory Block D is "
+ "enabled for Run_Time Hashing.\n");
+ rtic_ctrl &= ~RTIC_CTL_HASHONCE_MEMD_BLK_EN;
+ rtic_ctrl |= RTIC_CTL_RUNTIME_MEMD_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+
+ }
+ break;
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API allows to configure start address,block length of the memory
+ * and select (SHA1/SHA256) Hash algorithm to hash the data. Start address
+ * indicates starting location from where the data in the memory is to
+ * be hashed. The number of blocks that needs to be hashed is loaded in
+ * the block count register. There are four memory blocks available. The
+ * user can configure any one of these four memory blocks by passing their
+ * appropriate address and block length to be Hashed. SHA 256 is supported
+ * only in RTIC2. This API configures the memory block (A, B, C, D) into
+ * ONE_TIME Mode for Hashing of data in memory.
+ *
+ * @param start_addr Starting address of the memory to be hashed.
+ * @param blk_len Block length of data in memory.
+ * @param mem_blk Memory block to be hashed.
+ * @param hash_select Select the (SHA1/SHA256)Hash Algorithm.
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+
+rtic_ret rtic_configure_mem_blk(ulong start_addr, ulong blk_len,
+ rtic_memblk mem_blk, int hash_select)
+{
+ rtic_ret ret_val = RTIC_SUCCESS;
+ ulong rtic_sts, rtic_ctrl;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ /* Validating the parameters passed. Checking for start address is
+ * word boundary aligned. Checking for block length is multiple of
+ * 4 bytes. */
+ if ((!start_addr) || (blk_len > RTIC_MAX_BLOCK_LENGTH) ||
+ (!((start_addr % 4) == 0)) || (!((blk_len % 4) == 0))) {
+ return RTIC_FAILURE;
+ }
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (mem_blk) {
+ case RTIC_A1:
+ pr_debug("RTIC Module: Mem Block A1 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMAADDR1);
+ pr_debug("RTIC Module: Mem Block A1 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMALEN1);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMA_BLK_EN;
+ if (hash_select) {
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMA_BLK_EN;
+ }
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_A2:
+ pr_debug("RTIC Module: Mem Block A2 start address"
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMAADDR2);
+ pr_debug("RTIC Module: Mem Block A2 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMALEN2);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMA_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMA_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_B1:
+ pr_debug("RTIC Module: Mem Block B1 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMBADDR1);
+ pr_debug("RTIC Module: Mem Block B1 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMBLEN1);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMB_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMB_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+
+ break;
+
+ case RTIC_B2:
+ pr_debug("RTIC Module: Mem Block B2 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMBADDR2);
+ pr_debug("RTIC Module: Mem Block B2 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMBLEN2);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMB_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMB_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_C1:
+ pr_debug("RTIC Module: Mem Block C1 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMCADDR1);
+ pr_debug("RTIC Module: Mem Block C1 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMCLEN1);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMC_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMC_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+
+ break;
+
+ case RTIC_C2:
+ pr_debug("RTIC Module: Mem Block C2 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMCADDR2);
+ pr_debug("RTIC Module: Mem Block C2 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMCLEN2);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMC_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMC_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+
+ break;
+
+ case RTIC_D1:
+ pr_debug("RTIC Module: Mem Block D1 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMDADDR1);
+ pr_debug("RTIC Module: Mem Block D1 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMDLEN1);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMD_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMD_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+
+ break;
+
+ case RTIC_D2:
+ pr_debug("RTIC Module: Mem Block D2 start address "
+ "0x%08lX\n", start_addr);
+ __raw_writel(start_addr, RTIC_MEMDADDR2);
+ pr_debug("RTIC Module: Mem Block D2 block len "
+ "0x%08lX\n", blk_len);
+ __raw_writel(blk_len, RTIC_MEMDLEN2);
+ rtic_ctrl |= RTIC_CTL_HASHONCE_MEMD_BLK_EN;
+ if (hash_select)
+ rtic_ctrl |= RTIC_CTL_HASHALGO_MEMD_BLK_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API will configure to start the Hashing of data in memory
+ * either in One-Time Hash mode or Run-Time Hash mode.
+ *
+ * @param mode RTIC mode of operation (ONE_TIME or RUN_TIME).
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+rtic_ret rtic_start_hash(rtic_mode mode)
+{
+ rtic_ret ret_val = RTIC_SUCCESS;
+ ulong rtic_cmd, rtic_sts;
+ /* Read for RTIC Registers value. */
+ rtic_cmd = __raw_readl(RTIC_COMMAND);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+
+ switch (mode) {
+ case RTIC_ONE_TIME:
+ pr_debug("RTIC Module: Starts the One_time hashing"
+ "process \n");
+ rtic_cmd |= RTIC_CMD_HASH_ONCE;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ case RTIC_RUN_TIME:
+ pr_debug("RTIC Module: Starts the Run_time hashing"
+ "process \n");
+#ifndef CONFIG_ARCH_MX27
+ /* Check RUN Time Disable bit before starting RUN Time
+ Hashing. */
+ if ((rtic_cmd & RTIC_RUN_TIME_DISABLE) == RTIC_RUN_TIME_DISABLE) {
+ rtic_cmd &= ~RTIC_RUN_TIME_DISABLE;
+ }
+#endif /* CONFIG_ARCH_MX27 */
+ rtic_cmd |= RTIC_CMD_RUN_TIME_CHK;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+
+ return ret_val;
+}
+
+/*!
+ * This API will read the RTIC status register.
+ *
+ * @return Status of Hashing.
+ */
+ulong rtic_get_status(void)
+{
+ ulong rtic_sts;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ pr_debug("RTIC Module: Hashing status register value 0x%08lX\n ",
+ rtic_sts);
+ return rtic_sts;
+}
+
+/*!
+ * This API will read the RTIC control register.
+ *
+ * @return Control register value.
+ */
+ulong rtic_get_control(void)
+{
+ ulong rtic_ctrl;
+ /* Read for RTIC Registers value. */
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ pr_debug("RTIC Module: Hashing control register value 0x%08lX\n ",
+ rtic_ctrl);
+ return rtic_ctrl;
+}
+
+/*!
+ * This API enables or disables interrupt for RTIC module.
+ *
+ * @param irq_en To enable or disable interrupt.
+ *
+ * @return RTIC_SUCCESS RTIC Interrupt configured Successfully .\n
+ * RTIC_FAILURE Error RTIC Interrupt configured.
+ */
+rtic_ret rtic_configure_interrupt(rtic_interrupt irq_en)
+{
+ rtic_ret ret_val = RTIC_SUCCESS;
+ ulong rtic_ctrl, rtic_sts;
+ /* Read for RTIC Registers value. */
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (irq_en) {
+ case RTIC_INTERRUPT_ENABLE:
+ pr_debug("RTIC Module: RTIC Interrupt enabled.\n");
+ /* If in interrupt mode then, set the irq enable bit in
+ the RTIC control register */
+ rtic_ctrl |= RTIC_CTL_IRQ_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ case RTIC_INTERRUPT_DISABLE:
+ pr_debug("RTIC Module: RTIC Interrupt Disabled.\n");
+ /* If in polling mode, then disable the irq enable bit in
+ the RTIC Control register */
+ rtic_ctrl &= ~RTIC_CTL_IRQ_EN;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API reads the Fault address of the RTIC module.
+ *
+ * @return Fault address register value.
+ */
+ulong rtic_get_faultaddress(void)
+{
+ ulong rtic_faultaddr;
+ /* Read for RTIC Registers value. */
+ rtic_faultaddr = __raw_readl(RTIC_FAULTADDR);
+ pr_debug("RTIC Module: Hashing fault register value 0x%08lX\n ",
+ rtic_faultaddr);
+ return rtic_faultaddr;
+}
+
+/*!
+ * This API reads 160/256 bit hash result from Hash result register for
+ * SHA1/SHA256.The data is copied to the memory pointed by the input pointer.
+ * SHA256 is supported only in RTIC2.
+ *
+ * @param hash_result_reg Hashed Value.
+ * @param hash_reg Hash result register address.
+ * @param hash_num Number of Hash Values.
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+
+rtic_ret rtic_hash_read(rtic_hash_rlt * hash_result_reg, ulong hash_reg,
+ int hash_num)
+{
+ int i;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ for (i = 0; i < hash_num; i++) {
+ hash_result_reg->hash_result[i] = __raw_readl(hash_reg);
+ hash_reg += 4;
+ }
+ return ret_val;
+
+}
+
+/*!
+ * This API writes 160/256 bit hash result from input pointer to Hash
+ * result register.This is used after warm boot to restore the hash
+ * Value and continue with Run Time Hashing.It avoid doing One time
+ * Hashing again if it is done before Warm Boot.This feature is supported
+ * only for RTIC2.
+ *
+ * @param hash_result_reg Hashed Value.
+ * @param hash_reg Hash Register Address.
+ * @param hash_num Number of 4 bytes for a Hash value.
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+
+rtic_ret rtic_hash_write(rtic_hash_rlt * hash_result_reg, rtic_memblk mem_blk,
+ int hash_num)
+{
+ int i;
+ ulong hash_reg;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ hash_reg = RTIC_MEMAHASHRES0 + (mem_blk * 20);
+ for (i = 0; i < hash_num; i++) {
+ __raw_writel(hash_result_reg->hash_result[i], hash_reg);
+ hash_reg += 4;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API reads 160/256 bit hash result from Hash result register for
+ * SHA1/SHA256.The data is copied to the memory pointed by the input pointer.
+ * SHA256 is supported only in RTIC2.
+ *
+ * @param mem_blk Memory block to be hashed.
+ * @param mode RTIC mode of operation (ONE_TIME or RUN_TIME).
+ * @param hash_result_reg Hashed value.
+ *
+ * @return RTIC_SUCCESS Successfully hashed the data.\n
+ * RTIC_FAILURE Error in the parameters passed.
+ */
+rtic_ret
+rtic_hash_result(rtic_memblk mem_blk, rtic_mode mode,
+ rtic_hash_rlt * hash_result_reg)
+{
+ rtic_ret ret_val = RTIC_SUCCESS;
+ ulong rtic_sts, rtic_ctrl;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ switch (mode) {
+ case RTIC_ONE_TIME:
+ /* In the one-time hash, Check Done interrupt before
+ reading hash results. */
+ if ((rtic_sts & RTIC_DONE) == RTIC_DONE) {
+ switch (mem_blk) {
+ case RTIC_A1:
+ pr_debug("RTIC Module: Read mem blk A hash"
+ "result\n");
+ if ((rtic_ctrl & RTIC_CTL_HASHALGO_MEMA_BLK_EN)
+ == RTIC_CTL_HASHALGO_MEMA_BLK_EN) {
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMAHASHRES0, 8);
+ } else
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMAHASHRES0, 5);
+ break;
+
+ case RTIC_B1:
+ pr_debug("RTIC Module: Read mem blk B hash"
+ "result\n");
+ if ((rtic_ctrl & RTIC_CTL_HASHALGO_MEMB_BLK_EN)
+ == RTIC_CTL_HASHALGO_MEMB_BLK_EN) {
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMBHASHRES0, 8);
+ } else
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMBHASHRES0, 5);
+ break;
+
+ case RTIC_C1:
+ pr_debug("RTIC Module: Read mem blk C hash"
+ "result\n");
+ if ((rtic_ctrl & RTIC_CTL_HASHALGO_MEMC_BLK_EN)
+ == RTIC_CTL_HASHALGO_MEMC_BLK_EN) {
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMCHASHRES0, 8);
+ } else
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMCHASHRES0, 5);
+ break;
+
+ case RTIC_D1:
+ pr_debug("RTIC Module: Read mem blk D hash"
+ "result\n");
+ if ((rtic_ctrl & RTIC_CTL_HASHALGO_MEMD_BLK_EN)
+ == RTIC_CTL_HASHALGO_MEMD_BLK_EN) {
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMDHASHRES0, 8);
+ } else
+ rtic_hash_read(hash_result_reg,
+ RTIC_MEMDHASHRES0, 5);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ } else {
+ pr_debug("RTIC Module: Memory blocks are not hashed "
+ "for boot authentication.\n");
+ ret_val = RTIC_FAILURE;
+ }
+ break;
+
+ case RTIC_RUN_TIME:
+ switch (mem_blk) {
+ case RTIC_A1:
+ pr_debug("RTIC Module: run-time check doesn't update"
+ "Hash result value.\n");
+ ret_val = RTIC_FAILURE;
+ break;
+
+ case RTIC_B1:
+ pr_debug("RTIC Module: run-time check doesn't update"
+ "Hash result value.\n");
+ ret_val = RTIC_FAILURE;
+ break;
+
+ case RTIC_C1:
+ pr_debug("RTIC Module: run-time check doesn't update"
+ "Hash result value.\n");
+ ret_val = RTIC_FAILURE;
+ break;
+
+ case RTIC_D1:
+ pr_debug("RTIC Module: run-time check doesn't update"
+ "Hash result value.\n");
+ ret_val = RTIC_FAILURE;
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API configures RTIC DMA Burst Size. It configures maximum number of
+ * words to burst read through DMA. This cannot be modified during run-time.
+ *
+ * @param dma_burst Maximum DMA Burst Size.
+ *
+ * @return RTIC_SUCCESS RTIC DMA successfully configured.\n
+ * RTIC_FAILURE Error DMA configuration.
+ */
+rtic_ret rtic_dma_burst_read(rtic_dma_word dma_burst)
+{
+ ulong rtic_ctrl;
+ ulong rtic_dma_delay, rtic_sts;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ pr_debug("RTIC DMA burst read confirgured.\n");
+ /* Read for RTIC Registers value. */
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ rtic_dma_delay = __raw_readl(RTIC_DMA);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (dma_burst) {
+ case RTIC_DMA_1_WORD:
+ rtic_ctrl &= ~DMA_BURST_CLR;
+ rtic_ctrl |= DMA_1_WORD;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+ case RTIC_DMA_2_WORD:
+ rtic_ctrl &= ~DMA_BURST_CLR;
+ rtic_ctrl |= DMA_2_WORD;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+ case RTIC_DMA_4_WORD:
+ rtic_ctrl &= ~DMA_BURST_CLR;
+ rtic_ctrl |= DMA_4_WORD;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+ case RTIC_DMA_8_WORD:
+ rtic_ctrl &= ~DMA_BURST_CLR;
+ rtic_ctrl |= DMA_8_WORD;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+ case RTIC_DMA_16_WORD:
+ rtic_ctrl &= ~DMA_BURST_CLR;
+ rtic_ctrl |= DMA_16_WORD;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ break;
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API sets DMA throttle register to 0x00 during boot time to minimize the
+ * performance impact at startup.
+ *
+ * @return RTIC_SUCCESS DMA throttle register set to 0x00 successfully.
+ * RTIC_FAILURE Failure in programing DMA throttle register..
+ */
+rtic_ret rtic_hash_once_dma_throttle(void)
+{
+ ulong rtic_sts;
+ ulong rtic_ctrl;
+
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+
+ if (cpu_is_mx27() || (cpu_is_mx31_rev(CHIP_REV_1_0) == 1)) {
+ __raw_writel(0x00, RTIC_DMA);
+ } else if (cpu_is_mx31_rev(CHIP_REV_2_0) >= 1) {
+ /* Setting Hash Once DMA Throttle timer to Zero. */
+ rtic_ctrl = __raw_readl(RTIC_CONTROL);
+ rtic_ctrl &= ~HASH_ONCE_DMA_THROTTLE_CLR;
+ __raw_writel(rtic_ctrl, RTIC_CONTROL);
+ }
+
+ return ret_val;
+}
+
+/*! This API programs the DMA Programmable Timer to set to specify how many
+ * cycles to wait between DMA bus access.
+ *
+ * @param dma_delay DMA Bus Duty Cycle Delay.
+ *
+ * @return RTIC_SUCCESS RTIC DMA Duty Cycle delay set successfully.\n
+ * RTIC_FAILURE Failure in programing DMA bus delay.
+ */
+rtic_ret rtic_dma_delay(ulong dma_delay)
+{
+ ulong rtic_sts;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ __raw_writel(dma_delay, RTIC_DMA);
+ return ret_val;
+}
+
+/*!
+ * This API Configures DMA Watchdog timer.
+ *
+ * @param wd_timer DMA Watchdog timer value.
+ *
+ * @return RTIC_SUCCESS RTIC DMA Watchdog Timer set successfully.\n
+ * RTIC_FAILURE Failure in programing DMA Watchdog Timer.
+ *
+ */
+rtic_ret rtic_wd_timer(ulong wd_timer)
+{
+ ulong rtic_sts;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ __raw_writel(wd_timer, RTIC_WDTIMER);
+ return ret_val;
+}
+
+/*!
+ * This API is used for Software reset RTIC Module.
+ *
+ * @param rtic_rst To Enable and Disable RTIC SW Reset.
+ *
+ * @return RTIC_SUCCESS RTIC SW Reset Configured successfully.\n
+ * RTIC_FAILURE Failure in configuring.
+ */
+rtic_ret rtic_sw_reset(rtic_sw_rst rtic_rst)
+{
+ ulong rtic_cmd, rtic_sts;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_cmd = __raw_readl(RTIC_COMMAND);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (rtic_rst) {
+ case RTIC_RST_ENABLE:
+ pr_debug("RTIC Module: RTIC SW Reset enabled.\n");
+ rtic_cmd |= RTIC_CMD_SW_RESET;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ case RTIC_RST_DISABLE:
+ pr_debug("RTIC Module: RTIC SW Reset Disabled.\n");
+ rtic_cmd &= ~RTIC_CMD_SW_RESET;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
+
+/*!
+ * This API is used RTIC to clear IRQ.
+ *
+ * @param rtic_irq_clr To Clear RTIC IRQ.
+ *
+ * @return RTIC_SUCCESS Clear RTIC IRQ Successfully.\n
+ * RTIC_FAILURE Failure in Clearing RTIC IRQ.
+ */
+rtic_ret rtic_clr_irq(rtic_clear_irq rtic_irq_clr)
+{
+ ulong rtic_cmd, rtic_sts;
+ rtic_ret ret_val = RTIC_SUCCESS;
+ /* Read for RTIC Registers value. */
+ rtic_cmd = __raw_readl(RTIC_COMMAND);
+ rtic_sts = __raw_readl(RTIC_STATUS);
+ /* Check for RTIC Busy bit before writing into RTIC Registers. */
+ if ((rtic_sts & RTIC_BUSY) != 0) {
+ pr_debug("RTIC Module: RTIC is in BUSY in Hashing\n");
+ return RTIC_FAILURE;
+ }
+ switch (rtic_irq_clr) {
+ case RTIC_CLR_IRQ_ENABLE:
+ pr_debug("RTIC Module: RTIC SW Reset enabled.\n");
+ rtic_cmd |= RTIC_CMD_CLRIRQ;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ case RTIC_CLR_IRQ_DISABLE:
+ pr_debug("RTIC Module: RTIC SW Reset Disabled.\n");
+ rtic_cmd &= ~RTIC_CMD_CLRIRQ;
+ __raw_writel(rtic_cmd, RTIC_COMMAND);
+ break;
+
+ default:
+ ret_val = RTIC_FAILURE;
+ break;
+ }
+ return ret_val;
+}
diff --git a/drivers/mxc/security/mxc_rtic.h b/drivers/mxc/security/mxc_rtic.h
new file mode 100644
index 000000000000..13b73421670c
--- /dev/null
+++ b/drivers/mxc/security/mxc_rtic.h
@@ -0,0 +1,317 @@
+/*
+ * 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_rtic.h
+ *
+ * @brief The header file for Run-Time Integrity Checker (RTIC) module.
+ * This file contains all register defines and bit definition of RTIC module.
+ *
+ * @ingroup MXC_Security
+ */
+
+#ifndef __MXC_RTIC_H__
+#define __MXC_RTIC_H__
+
+#include <asm/arch/mxc_security_api.h>
+#include <asm/arch/hardware.h>
+#include <asm-generic/errno-base.h>
+
+/*
+ * RTIC status register
+ */
+#define RTIC_STATUS IO_ADDRESS(RTIC_BASE_ADDR + 0x00)
+
+/*
+ * RTIC Command Register
+ */
+#define RTIC_COMMAND IO_ADDRESS(RTIC_BASE_ADDR + 0x04)
+
+/*
+ * RTIC Control Register
+ */
+#define RTIC_CONTROL IO_ADDRESS(RTIC_BASE_ADDR + 0x08)
+
+/*
+ * RTIC Delay Timer/DMA Throttle
+ */
+#define RTIC_DMA IO_ADDRESS(RTIC_BASE_ADDR + 0x0C)
+
+/*
+ * RTIC Memory A Address 1
+ */
+#define RTIC_MEMAADDR1 IO_ADDRESS(RTIC_BASE_ADDR + 0x10)
+
+/*
+ * RTIC Memory A Length 1
+ */
+#define RTIC_MEMALEN1 IO_ADDRESS(RTIC_BASE_ADDR + 0x14)
+
+/*
+ * RTIC Memory A Address 2
+ */
+#define RTIC_MEMAADDR2 IO_ADDRESS(RTIC_BASE_ADDR + 0x18)
+/*
+ * RTIC Memory A Length 2
+ */
+#define RTIC_MEMALEN2 IO_ADDRESS(RTIC_BASE_ADDR + 0x1C)
+
+/*
+ * RTIC Memory B Address 1
+ */
+#define RTIC_MEMBADDR1 IO_ADDRESS(RTIC_BASE_ADDR + 0x30)
+
+/*
+ * RTIC Memory B Length 1
+ */
+#define RTIC_MEMBLEN1 IO_ADDRESS(RTIC_BASE_ADDR + 0x34)
+
+/*
+ * RTIC Memory B Address 2
+ */
+#define RTIC_MEMBADDR2 IO_ADDRESS(RTIC_BASE_ADDR + 0x38)
+
+/*
+ * RTIC Memory B Length 2
+ */
+#define RTIC_MEMBLEN2 IO_ADDRESS(RTIC_BASE_ADDR + 0x3C)
+
+/*
+ * RTIC Memory C Address 1
+ */
+#define RTIC_MEMCADDR1 IO_ADDRESS(RTIC_BASE_ADDR + 0x50)
+
+/*
+ * RTIC Memory C Length 1
+ */
+#define RTIC_MEMCLEN1 IO_ADDRESS(RTIC_BASE_ADDR + 0x54)
+
+/*
+ * RTIC Memery C Address 2
+ */
+#define RTIC_MEMCADDR2 IO_ADDRESS(RTIC_BASE_ADDR + 0x58)
+
+/*
+ * RTIC Memory C Length 2
+ */
+#define RTIC_MEMCLEN2 IO_ADDRESS(RTIC_BASE_ADDR + 0x5C)
+
+/*
+ * RTIC Memory D Address 1
+ */
+#define RTIC_MEMDADDR1 IO_ADDRESS(RTIC_BASE_ADDR + 0x70)
+
+/*
+ * RTIC Memory D Length 1
+ */
+#define RTIC_MEMDLEN1 IO_ADDRESS(RTIC_BASE_ADDR + 0x74)
+
+/*
+ * RTIC Memory D Address 2
+ */
+#define RTIC_MEMDADDR2 IO_ADDRESS(RTIC_BASE_ADDR + 0x78)
+
+/*
+ * RTIC Memory D Length 2
+ */
+#define RTIC_MEMDLEN2 IO_ADDRESS(RTIC_BASE_ADDR + 0x7C)
+
+/*
+ * RTIC Fault address Register.
+ */
+#define RTIC_FAULTADDR IO_ADDRESS(RTIC_BASE_ADDR + 0x90)
+
+/*
+ * RTIC Watchdog Timeout
+ */
+#define RTIC_WDTIMER IO_ADDRESS(RTIC_BASE_ADDR + 0x94)
+
+/*
+ * RTIC Memory A Hash Result Word A
+ */
+#define RTIC_MEMAHASHRES0 IO_ADDRESS(RTIC_BASE_ADDR + 0xA0)
+
+/*
+ * RTIC Memory B Hash Result Word A
+ */
+#define RTIC_MEMBHASHRES0 IO_ADDRESS(RTIC_BASE_ADDR + 0xC0)
+
+/*
+ * RTIC Memory C Hash Result Word A
+ */
+#define RTIC_MEMCHASHRES0 IO_ADDRESS(RTIC_BASE_ADDR + 0xE0)
+/*
+ * RTIC Memory D Hash Result Word A
+ */
+#define RTIC_MEMDHASHRES0 IO_ADDRESS(RTIC_BASE_ADDR + 0x100)
+/*
+ * RTIC Memory Integrity Status
+ */
+#define RTIC_STAT_MEMBLK 0x100
+
+/*
+ * RTIC Address Error
+ */
+#define RTIC_STAT_ADDR_ERR 0x200
+
+/*
+ * RTIC Length Error
+ */
+#define RTIC_STAT_LEN_ERR 0x400
+
+/*
+ * RTIC Clear Interrupts command
+ */
+#define RTIC_CMD_CLRIRQ 0x01
+
+/*
+ * RTIC SW reset command
+ */
+#define RTIC_CMD_SW_RESET 0x02
+
+/*
+ * RTIC Hash Once command
+ */
+#define RTIC_CMD_HASH_ONCE 0x04
+
+/*
+ * RTIC Run Time Check Command
+ */
+#define RTIC_CMD_RUN_TIME_CHK 0x08
+
+/*
+ * RTIC IRQ EN
+ */
+#define RTIC_CTL_IRQ_EN 0x01
+
+/*
+ * RTIC Hash ALGO 256 Memory block A enable
+ */
+#define RTIC_CTL_HASHALGO_MEMA_BLK_EN 0x1000
+
+/*
+ * RTIC Hash ALGO 256 Memory block B Enable
+ */
+#define RTIC_CTL_HASHALGO_MEMB_BLK_EN 0x2000
+
+/*
+ * RTIC Hash ALGO 256 Memory block C Enable
+ */
+#define RTIC_CTL_HASHALGO_MEMC_BLK_EN 0x4000
+
+/*
+ * RTIC Hash ALGO 256 Memory block D Enable
+ */
+#define RTIC_CTL_HASHALGO_MEMD_BLK_EN 0x8000
+
+/*
+ * RTIC Hash Once Memory block A enable
+ */
+#define RTIC_CTL_HASHONCE_MEMA_BLK_EN 0x10
+
+/*
+ * RTIC Hash Once Memory block B Enable
+ */
+#define RTIC_CTL_HASHONCE_MEMB_BLK_EN 0x20
+
+/*
+ * RTIC Hash Once Memory block C Enable
+ */
+#define RTIC_CTL_HASHONCE_MEMC_BLK_EN 0x40
+
+/*
+ * RTIC Hash Once Memory block D Enable
+ */
+#define RTIC_CTL_HASHONCE_MEMD_BLK_EN 0x80
+
+/*
+ * RTIC Run Time Memory blk A Enable
+ */
+#define RTIC_CTL_RUNTIME_MEMA_BLK_EN 0x100
+
+/*
+ * RTIC Run Time Memory blk B Enable
+ */
+#define RTIC_CTL_RUNTIME_MEMB_BLK_EN 0x200
+
+/*
+ * RTIC Run Time Memory blk C Enable
+ */
+#define RTIC_CTL_RUNTIME_MEMC_BLK_EN 0x400
+
+/*
+ * RTIC Run Time Memory blk D Enable
+ */
+#define RTIC_CTL_RUNTIME_MEMD_BLK_EN 0x800
+
+/*!
+ * Maximum block length that can be configured to the RTIC module.
+ */
+#define RTIC_MAX_BLOCK_LENGTH 0xFFFFFFFC
+
+/*!
+ * This define is used to clear DMA Burst in RTIC Control register.
+ */
+#define DMA_BURST_CLR 0xE
+
+//#ifndef CONFIG_ARCH_MX27
+/*!
+ * This define will clear Hash Once DMA Throttle Programmable timer.
+ */
+#define HASH_ONCE_DMA_THROTTLE_CLR (0xFF << 16)
+
+//#endif /* CONFIG_ARCH_MX27 */
+
+/*!
+ * This define is used to configure DMA Burst read as 1 word.
+ */
+#define DMA_1_WORD 0x00
+
+/*!
+ * This define is used to configure DMA Burst read as 2 words.
+ */
+#define DMA_2_WORD 0x02
+
+/*!
+ * This define is used to configure DMA Burst read as 4 words.
+ */
+#define DMA_4_WORD 0x04
+
+/*!
+ * This define is used to configure DMA Burst read as 8 words.
+ */
+#define DMA_8_WORD 0x06
+
+/*!
+ * This define is used to configure DMA Burst read as 16 words.
+ */
+#define DMA_16_WORD 0x08
+
+/*!
+ * This define is used for checking RTIC is busy or not.
+ */
+#define RTIC_BUSY 0x01
+
+#ifndef CONFIG_ARCH_MX27
+/*!
+ * This define is used for checking RUN Time Disable bit is set or not.
+ */
+#define RTIC_RUN_TIME_DISABLE 0x10
+
+#endif /* CONFIG_ARCH_MX27 */
+
+/*!
+ * This define is used to check Done bit set in RTIC Status register.
+ */
+#define RTIC_DONE 0x02
+#endif /* __MXC_RTIC_H__ */
diff --git a/drivers/mxc/security/mxc_scc.c b/drivers/mxc/security/mxc_scc.c
new file mode 100644
index 000000000000..c3b41d49904f
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc.c
@@ -0,0 +1,2297 @@
+/*
+ * 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_scc.c
+ *
+ * @brief 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 <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include "mxc_scc_internals.h"
+
+
+/******************************************************************************
+ *
+ * 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.
+ */
+//#define static
+static void *scc_base;
+static struct clk *scc_clk;
+
+/*! 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_5,
+ .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;
+
+/*! The lookup table for an 8-bit value. Calculated once
+ * by #scc_init_ccitt_crc().
+ */
+static uint16_t scc_crc_lookup_table[256];
+uint8_t make_vpu_partition(void);
+
+/*! 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 };
+
+
+/*!
+ * 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)
+
+/*!
+ * This function is called to put the SCC in a low power state.
+ *
+ * @param pdev the device structure used to give information on which SCC
+ * device (0 through 3 channels) to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_scc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ pr_debug(" MXC SCC driver suspend function\n");
+ /* Turn off clock */
+ clk_disable(scc_clk);
+ return 0;
+}
+
+/*!
+ * This function is called to resume the SCC from a low power state.
+ *
+ * @param pdev the device structure used to give information on which SCC
+ * device (0 through 3 channels) to suspend
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_scc_resume(struct platform_device *pdev)
+{
+ pr_debug("MXC SCC driver resume function\n");
+ /* Turn on clock */
+ clk_enable(scc_clk);
+
+ return 0;
+}
+
+static int mxc_scc_probe(struct platform_device *pdev);
+static int mxc_scc_remove(struct platform_device *pdev);
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_scc_driver = {
+ .driver = {
+ .name = "mxc_scc",
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = mxc_scc_probe,
+ .remove = mxc_scc_remove,
+ .suspend = mxc_scc_suspend,
+ .resume = mxc_scc_resume,
+};
+
+#undef static
+/*!
+ * Registering the SCC driver
+ *
+ */
+static int scc_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&mxc_scc_driver);
+ return ret;
+}
+
+/******************************************************************************
+ *
+ * Function Implementations - Externally Accessible
+ *
+ *****************************************************************************/
+
+/*****************************************************************************/
+/* fn mxc_scc_probe() */
+/*****************************************************************************/
+/*!
+ * 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 mxc_scc_probe(struct platform_device *pdev)
+{
+ uint32_t smn_status;
+ int i;
+ int return_value = -EIO; /* assume error */
+ /* Enable the SCC clocks */
+ pr_debug(KERN_ALERT "SCC: Enabling the SCC CLK ... \n");
+ scc_clk = clk_get(NULL, "scc_clk");
+ clk_enable(scc_clk);
+ 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 */
+ }
+
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ printk
+ ("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 = ioremap_nocache(SCC_BASE, SCC_ADDRESS_RANGE);
+
+ /* If that worked, we can try to use the SCC */
+ if (scc_base == NULL) {
+ pr_debug
+ ("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) {
+ /**
+ * 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
+ if (!smn_irq_set) { /* Separate. Check SMN binding */
+#elif !defined(NO_SMN_INTERRUPT)
+ if (!scm_irq_set) { /* Shared. Check SCM binding */
+#else
+ if (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) {
+ mxc_scc_remove(pdev);
+ } 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;
+} /* mxc_scc_probe */
+
+/*****************************************************************************/
+/* fn mxc_scc_remove() */
+/*****************************************************************************/
+/*!
+ * 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 int mxc_scc_remove(struct platform_device *pdev)
+{
+ 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) {
+ free_irq(INT_SCC_SCM, NULL);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ free_irq(INT_SCC_SMN, NULL);
+#endif
+ }
+ pr_debug("SCC driver cleaned up.\n");
+ return 0;
+
+} /* mxc_scc_remove */
+
+static void scc_cleanup(void)
+{
+ platform_driver_unregister(&mxc_scc_driver);
+}
+
+/*****************************************************************************/
+/* fn scc_get_configuration() */
+/*****************************************************************************/
+scc_config_t *scc_get_configuration(void)
+{
+
+ /**
+ * 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_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, uint8_t * data_in,
+ 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;
+
+
+ (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() 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)
+{
+
+
+ /* 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;
+
+
+ /* 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;
+
+
+ /* 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;
+
+
+ /* 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;
+
+
+ /* 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.
+ *
+ * @param irq Channel number for the IRQ. (@c SCC_INT_SMN or @c SCC_INT_SCM).
+ * @param dev_id Pointer to the identification of the device. Ignored.
+ */
+static irqreturn_t scc_irq(int irq, void *dev_id)
+{
+ 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 (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 (irq == INT_SCC_SCM && scm_status & SCM_STATUS_INTERRUPT_STATUS) {
+ /* 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 */
+
+#ifdef __BIG_ENDIAN
+ 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) {
+#ifdef __BIG_ENDIAN
+ 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 */
+
+#ifdef __BIG_ENDIAN
+ 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();
+ pr_debug("SCC: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ pr_debug("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
+ *
+ */
+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;
+#ifdef CONFIG_VIRTIO_SUPPORT
+ scc_configuration.red_ram_size_blocks = 128;
+#endif
+
+ scc_configuration.black_ram_size_blocks = (config_register &
+ SCM_CFG_BLACK_SIZE_MASK)
+ >> SCM_CFG_BLACK_SIZE_SHIFT;
+#ifdef CONFIG_VIRTIO_SUPPORT
+ scc_configuration.black_ram_size_blocks = 128;
+#endif
+
+ 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 = request_irq(INT_SCC_SMN, scc_irq, 0,
+ SCC_DRIVER_NAME, NULL);
+ if (smn_error_code != 0) {
+ pr_debug
+ ("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 = request_irq(INT_SCC_SCM, scc_irq, 0,
+ SCC_DRIVER_NAME, NULL);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ pr_debug
+ ("SCC Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ pr_debug
+ ("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, 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 */
+ uint32_t scm_location = SCM_RED_MEMORY + SCM_NON_RESERVED_OFFSET; /* byte address into SCM RAM */
+ uint32_t scm_bytes_remaining = scc_memory_size_bytes; /* free RED RAM */
+ uint8_t padding_buffer[PADDING_BUFFER_MAX_BYTES]; /* CRC+padding holder */
+ unsigned padding_byte_count = 0; /* Reset if padding required */
+ uint32_t scm_error_status = 0; /* No known SCM error initially */
+
+ /* 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 */
+
+ /* 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, 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 */
+ uint32_t scm_location = SCM_BLACK_MEMORY + SCM_NON_RESERVED_OFFSET; /* next target for ctext */
+ 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 */
+
+ 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 */
+ 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;
+
+ /* 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);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* fn verify_slot_access() */
+/*****************************************************************************/
+static inline scc_return_t verify_slot_access(uint64_t owner_id, uint32_t slot,
+ uint32_t access_len)
+{
+ register scc_return_t status;
+
+ 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);
+ }
+ status = SCC_RET_FAIL;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* 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, 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 {
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_load_slot */
+
+/*****************************************************************************/
+/* fn scc_encrypt_slot() */
+/*****************************************************************************/
+/*!
+ * 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.
+ */
+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;
+}
+uint8_t make_vpu_partition() {
+ printk(KERN_INFO"No VPU partition allocated\n");
+ return 0;
+}
+
+/*****************************************************************************/
+/* fn scc_wait_completion() */
+/*****************************************************************************/
+/*!
+ * Poll looking for end-of-cipher indication. Only used
+ * if @c SCC_SCM_SLEEP is not defined.
+ *
+ * @internal
+ *
+ * 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)) {
+ /* kill time if loop not optimized away */
+ udelay(1000);
+ }
+ 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 = __raw_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)__raw_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..e19cbd4c3e72
--- /dev/null
+++ b/drivers/mxc/security/mxc_scc_internals.h
@@ -0,0 +1,325 @@
+/*
+ * 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_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
+ */
+
+#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 <asm/io.h> /* ioremap() */
+#include <asm/arch/mxc_scc_driver.h>
+
+/* Get handle on certain per-platform symbols */
+
+#include <asm/arch/iim.h>
+#include <asm/arch/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)
+
+/*!
+ * Turn on generation of run-time operational, debug, and error messages
+ */
+
+/*!
+ * 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
+
+/*!
+ * 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
+
+#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)
+
+/* 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
+
+/*! 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 __raw_readl()/__raw_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 __raw_readl()/__raw_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"
+
+/* 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 */
+static irqreturn_t scc_irq(int irq, void *dev_id);
+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);
+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);
+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 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);
+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_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);
+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);
+
+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/mxc_sec_mod.c b/drivers/mxc/security/mxc_sec_mod.c
new file mode 100644
index 000000000000..eeb5cb415600
--- /dev/null
+++ b/drivers/mxc/security/mxc_sec_mod.c
@@ -0,0 +1,67 @@
+/*
+ * 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 <asm/arch/mxc_security_api.h>
+
+static int __init mxc_sec_mod_init(void)
+{
+ printk(KERN_INFO "SEC: mxc_sec_mod_init() called \n");
+ return 0;
+}
+
+static void __exit mxc_sec_mod_cleanup(void)
+{
+ printk(KERN_INFO "SEC: mxc_sec_mod_cleanup() called \n");
+}
+
+#ifndef CONFIG_ARCH_MX3
+#ifdef CONFIG_MXC_SECURITY_HAC
+/* Export Symbol of HACC */
+EXPORT_SYMBOL(hac_hash_data);
+EXPORT_SYMBOL(hac_hashing_status);
+EXPORT_SYMBOL(hac_get_status);
+EXPORT_SYMBOL(hac_stop);
+EXPORT_SYMBOL(hac_hash_result);
+EXPORT_SYMBOL(hac_swrst);
+EXPORT_SYMBOL(hac_burst_mode);
+EXPORT_SYMBOL(hac_burst_read);
+#ifdef CONFIG_PM
+EXPORT_SYMBOL(hac_suspend);
+EXPORT_SYMBOL(hac_resume);
+#endif
+#endif
+#endif
+
+#ifdef CONFIG_MXC_SECURITY_RTIC
+/* Export Symbol of RTIC */
+EXPORT_SYMBOL(rtic_init);
+EXPORT_SYMBOL(rtic_configure_mode);
+EXPORT_SYMBOL(rtic_configure_mem_blk);
+EXPORT_SYMBOL(rtic_start_hash);
+EXPORT_SYMBOL(rtic_get_status);
+EXPORT_SYMBOL(rtic_get_control);
+EXPORT_SYMBOL(rtic_configure_interrupt);
+EXPORT_SYMBOL(rtic_hash_result);
+EXPORT_SYMBOL(rtic_hash_write);
+EXPORT_SYMBOL(rtic_get_faultaddress);
+EXPORT_SYMBOL(rtic_dma_burst_read);
+EXPORT_SYMBOL(rtic_hash_once_dma_throttle);
+EXPORT_SYMBOL(rtic_dma_delay);
+EXPORT_SYMBOL(rtic_wd_timer);
+EXPORT_SYMBOL(rtic_sw_reset);
+EXPORT_SYMBOL(rtic_clr_irq);
+#endif
+
+module_init(mxc_sec_mod_init);
+module_exit(mxc_sec_mod_cleanup);
diff --git a/drivers/mxc/security/rng/Makefile b/drivers/mxc/security/rng/Makefile
new file mode 100644
index 000000000000..7365e7d3d382
--- /dev/null
+++ b/drivers/mxc/security/rng/Makefile
@@ -0,0 +1,29 @@
+# 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) += rng.o
+rng-objs := rng_driver.o shw_driver.o
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..5089a08f18bf
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_driver.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2005-2007 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_RNGC)
+
+#include "rng_rngc.h"
+
+#else /* neither RNGA nor RNGC */
+
+#error NO_RNG_TYPE_IDENTIFIED
+
+#endif
+
+/*!****************************************************************************
+ * Enumerations
+ *****************************************************************************/
+
+/*! Values from Version ID register */
+enum rng_type {
+ /*! Type RNGA. Really is unused bits from the Control Register of the
+ RNGA. */
+ RNG_TYPE_RNGA = 0,
+ /*! Type B. Unsupported by this driver. */
+ 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 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..73251e9cc282
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_internals.h
@@ -0,0 +1,613 @@
+/*
+ * Copyright 2005-2007777777 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public 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"
+
+#include <asm/arch/mxc_scc_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 */
+
+/*!
+ * 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
+
+/*! Interrupt number for driver. */
+#define INT_RNG INT_RNGA
+
+/*! 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 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_RNGC */
+
+/*! Interrupt number for driver. */
+#define INT_RNG INT_RNGC
+/*! Base (bus?) address of RNG component. */
+#define RNG_BASE_ADDR RNGC_BASE_ADDR
+
+/*! 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.
+ */
+#define RNG_VERIFY_TYPE(type) \
+ ((type) == RNG_TYPE_RNGC)
+
+/*! 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.
+ **/
+#define RNG_ADD_ENTROPY(value)
+/*! 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() 1
+
+/*! 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);
+static void rng_sec_failure(void);
+
+#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..0aa36d0bef67
--- /dev/null
+++ b/drivers/mxc/security/rng/include/rng_rngc.h
@@ -0,0 +1,203 @@
+/*
+ * 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_RNGC_H
+#define RNG_RNGC_H
+
+#define RNGC_VERSION_MAJOR3 3
+
+/*! 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
+/*! RNGC Verification Control Register1 R/W */
+#define RNGC_VERIFICATION_CONTROL 0x0020
+/*! RNGC Oscillator Counter Control Register1 R/W */
+#define RNGC_OSCILLATOR_CONTROL_COUNTER 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 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 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 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 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 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
+/*! 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 RNGC Error Status Register Definitions
+ * @ingroup RNG
+ */
+/*! @addtogroup rngcerrstatusreg */
+/*! @{ */
+/*! Unused or MBZ. */
+#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffffc0
+/*! 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 RNGC, in bytes */
+#define RNG_ADDRESS_RANGE 0x34
+
+#endif
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..06f3f186466a
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_driver.h
@@ -0,0 +1,2141 @@
+/*
+ * Copyright 2005-2007 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.
+ * @ingroup RNG
+ */
+
+/*! Perform a security function. */
+#define SHW_IOCTL_REQUEST 21
+
+/*!
+ * 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_type {
+ 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_request_t;
+
+/*!****************************************************************************
+ * Enumerations
+ *****************************************************************************/
+/*!
+ * Flags for the state of the User Context Object (#fsl_shw_uco_t).
+ */
+typedef enum fsl_shw_user_ctx_flags {
+ FSL_UCO_BLOCKING_MODE = 0x01, /*!< 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_CALLBACK_MODE = 0x02, /*!< 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_SAVE_DESC_CHAIN = 0x04, /*!< Do not free descriptor chain after
+ driver (adaptor) finishes */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 0x08, /*!< User has made at least one
+ request with callbacks requested, so
+ API is ready to handle others. */
+
+ FSL_UCO_CHAIN_PREPHYSICALIZED = 0x10, /*!< (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_CONTEXT_CHANGED = 0x20, /*!< The user has changed the context but
+ the changes have not been copied to the
+ kernel driver. */
+ FSL_UCO_USERMODE_USER = 0x40, /*!< Internal use. This context belongs to a
+ user-mode API 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.
+ */
+/* REQ-FSLSHW-ERR-001 */
+typedef enum fsl_shw_return_t {
+ FSL_RETURN_OK_S = 0, /*!< 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_ERROR_S, /*!< Failure for non-specific reason. */
+ FSL_RETURN_NO_RESOURCE_S, /*!< Operation failed because some resource was
+ not able to be allocated. */
+ FSL_RETURN_BAD_ALGORITHM_S, /*!< Crypto algorithm unrecognized or
+ improper. */
+ FSL_RETURN_BAD_MODE_S, /*!< Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_FLAG_S, /*!< Flag setting unrecognized or
+ inconsistent. */
+ FSL_RETURN_BAD_KEY_LENGTH_S, /*!< Improper or unsupported key length for
+ algorithm. */
+ FSL_RETURN_BAD_KEY_PARITY_S, /*!< Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /*!< Improper or unsupported data length for
+ algorithm or internal buffer. */
+ FSL_RETURN_AUTH_FAILED_S, /*!< Authentication failed in
+ authenticate-decrypt operation. */
+ FSL_RETURN_MEMORY_ERROR_S, /*!< A memory error occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S /*!< An error internal to the hardware
+ occurred. */
+} 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 {
+ 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 {
+ 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_USE_SECRET_KEY = 8, /*!< Use device-unique key. Not always
+ available. */
+} 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 {
+ 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 {
+ SHW_WORK_GET_RANDOM = 1, /*!< fsl_shw_get_random() request. */
+ SHW_WORK_ADD_RANDOM, /*!< fsl_shw_add_entropy() request. */
+} shw_work_type_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 {
+ struct shw_queue_entry *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 * 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 {
+ struct shw_queue_entry *head; /*!< First entry in queue. */
+ struct shw_queue_entry *tail; /*!< Last entry. */
+} shw_queue_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 */
+#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_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;
+
+/* 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. */
+} 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 */
+} 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[259]; /*!< Stored context. Large enough
+ for ARC4. */
+} 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;
+
+#endif
+
+/*!****************************************************************************
+ * Access Macros for Objects
+ *****************************************************************************/
+/*!
+ * Get FSL SHW API version
+ *
+ * @param pcobject The Platform Capababilities Object to query.
+ * @param pcmajor A pointer to where the major version
+ * of the API is to be stored.
+ * @param 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 pcmajor A pointer to where the major version
+ * of the driver is to be stored.
+ * @param 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 pcalgorithms A pointer to where to store the location of
+ * the list of algorithms.
+ * @param 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 gsmodes A pointer to where to store the location of
+ * the list of modes.
+ * @param 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 gsalgorithms A pointer which will be set to the list of
+ * algorithms.
+ * @param 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.
+ */
+#define fsl_shw_pco_check_sym_supported(pcobject, pcalg, pcmode) \
+ 0
+
+/*!
+ * 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.
+ */
+#define fsl_shw_pco_check_black_key_supported(pcobject) \
+ 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.
+ */
+#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; \
+} while (0)
+
+/*!
+ * 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
+
+/*!
+ * 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) \
+do { \
+ (skobject)->algorithm = skalgorithm; \
+ (skobject)->flags = 0; \
+} while (0)
+
+/*!
+ * 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) \
+do { \
+ (skobject)->key_length = skkeylen; \
+ memcpy((skobject)->key, skkey, skkeylen); \
+ (skobject)->flags |= FSL_SKO_KEY_PRESENT; \
+ (skobject)->flags &= ~FSL_SKO_KEY_ESTABLISHED; \
+} while (0)
+
+/*!
+ * 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)
+
+/*!
+ * 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) \
+do { \
+ (skobject)->userid = (skuserid); \
+ (skobject)->handle = (skhandle); \
+ (skobject)->flags |= FSL_SKO_KEY_ESTABLISHED; \
+ (skobject)->flags &= \
+ ~(FSL_SKO_KEY_PRESENT); \
+} while (0)
+
+/*!
+ * 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 skalgorithm A pointer to the location to store the algorithm.
+ */
+#define fsl_shw_sko_get_algorithm(skobject, skalgorithm) \
+ *(skalgorithm) = (skobject)->algorithm
+
+/*!
+ * 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) \
+do { \
+ if ((wkeyinfo)->key_length > 32) { \
+ *(wkeylen) = 0; \
+ } else { \
+ *(wkeylen) = 66; \
+ } \
+} while (0)
+
+/*!
+ * 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)
+
+/* 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 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 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 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 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 sccounter Pointer to location to store the current counter
+ * value. The number of octets copied will be the
+ * block size for the algorithm.
+ * @param 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-006 */
+/*!
+ * 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);
+
+/*!
+ * Retrieve results from earlier operations.
+ *
+ * @param user_ctx The user's context.
+ * @param result_size The number of array elements of @a results.
+ * @param results Pointer to first of the (array of) locations to
+ * store results.
+ * @param 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 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 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);
+
+/* 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 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 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 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 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 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 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 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 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 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 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 ct Pointer to the where the encrypted @a payload
+ * will be stored. Must be @a payload_length
+ * octets long.
+ * @param 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 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);
+
+/*!***************************************************************************
+ *
+ * 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", code,
+ strerror(code));
+#endif
+ }
+
+ if (blocking) {
+ free(req);
+ }
+
+ return ret;
+}
+
+#endif /* no __KERNEL__ */
+
+#endif /* SHW_DRIVER_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..630ceb1ee377
--- /dev/null
+++ b/drivers/mxc/security/rng/include/shw_internals.h
@@ -0,0 +1,158 @@
+/*
+ * 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 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.
+ *
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "shw_driver.h"
+
+#include <asm/arch/mxc_scc_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 */
+
+/*! @} */
+#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
+#ifdef __KERNEL__
+static fsl_shw_uco_t *user_list;
+#endif
+/*!
+ * 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);
+
+#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..55fe33ea5165
--- /dev/null
+++ b/drivers/mxc/security/rng/rng_driver.c
@@ -0,0 +1,1067 @@
+/*
+ * 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 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"
+
+/* 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_RNGC)
+ 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");
+ clk_enable(clk);
+
+ 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;
+ pr_debug("RNG: Driver failed to map RNG registers. %d\n",
+ return_code);
+ goto check_err;
+ }
+ pr_debug("RNG Driver: rng_base is 0x%08x\n", (uint32_t) rng_base);
+ /*Check SCC keys are fused */
+ if (RNG_HAS_ERROR()) {
+ if (RNG_HAS_BAD_KEY()) {
+ printk(KERN_INFO "ERROR: BAD KEYS SELECTED\n");
+ 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 */
+ RNG_MASK_ALL_INTERRUPTS();
+ RNG_WAKE();
+
+ /* Determine status of RNG */
+ if (RNG_OSCILLATOR_FAILED()) {
+ pr_debug("RNG Driver: RNG Oscillator is dead\n");
+ 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();
+ RNG_SELF_TEST();
+#if defined(FSL_HAVE_RNGC)
+ 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_RNGC)
+ 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()) {
+ pr_debug("RNG Driver: RNG is in High Assurance mode\n");
+ } else {
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ pr_debug("RNG Driver: RNG could not be put in "
+ "High Assurance mode\n");
+#endif
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ /* Check that RNG is OK */
+ if (!RNG_WORKING()) {
+ pr_debug("RNG determined to be inoperable."
+ " Status %08x\n", 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) {
+ pr_debug("RNG: lock initialization failed\n");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ }
+
+ return_code = os_create_task(rng_entropy_task);
+ if (return_code != OS_ERROR_OK_S) {
+ pr_debug("RNG: task initialization failed\n");
+ rng_availability = RNG_STATUS_FAILED;
+ goto check_err;
+ } else {
+ task_started = TRUE;
+ }
+#if defined(FSL_HAVE_RNGA)
+ scc_code = scc_monitor_security_failure(rng_sec_failure);
+ if (scc_code != SCC_RET_OK) {
+ pr_debug
+ ("RNG Driver: Failed to register SCC callback: %d\n",
+ scc_code);
+#ifndef RNG_NO_FORCE_HIGH_ASSURANCE
+ return_code = OS_ERROR_FAIL_S;
+ goto check_err;
+#endif
+ }
+#endif
+ 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) {
+ pr_debug("RNG: Driver initialization failed. %d\n",
+ 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)
+{
+
+ pr_debug("RNG: shutdown called\n");
+ 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;
+ scc_stop_monitoring_security_failure(rng_sec_failure);
+ 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;
+ }
+ rng_availability = RNG_STATUS_FAILED;
+ } else {
+ rng_availability = RNG_STATUS_UNIMPLEMENTED;
+ }
+
+ pr_debug("RNG Driver: Cleaned up\n");
+
+} /* 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)) {
+ /* 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;
+#ifdef FSL_HAVE_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 /* RNGC */
+ (secure &&
+ ((offset == RNGC_FIFO) ||
+ (offset == RNGC_VERIFICATION_CONTROL) ||
+ (offset == RNGC_OSCILLATOR_CONTROL_COUNTER) ||
+ (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_RNGA
+ if (!(offset == RNGA_ENTROPY)) {
+ return_code = TRUE; /* Let all others be read */
+ } else {
+ pr_debug
+ ("RNG: Offset %04x denied read access\n",
+ offset);
+ }
+#else /* else RNGC */
+ /* No registers are write-only */
+ return_code = TRUE;
+#endif /* RNGA */
+ } /* 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_RNGC */
+ ((offset == RNGC_STATUS) ||
+ (offset == RNGC_FIFO) ||
+ (offset == RNGC_OSC_COUNTER) ||
+ (offset == RNGC_OSC_COUNTER_STATUS))
+#endif
+ ) {
+ return_code = TRUE; /* can be written */
+ } else {
+ pr_debug
+ ("RNG: Offset %04x denied write access\n",
+ offset);
+ }
+ } /* write */
+ } /* not high assurance and inaccessible register... */
+ else {
+ pr_debug("RNG: Offset %04x denied high-assurance access\n",
+ 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 */
+
+ pr_debug("RNG Driver: Inside the RNG Interrupt Handler\n");
+ 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;
+
+ /* Map the RNG memory on the internal bus into kernel address
+ space */
+ rng_base = os_map_device(RNG_BASE_ADDR, RNG_ADDRESS_RANGE);
+ if (rng_base == NULL) {
+ /* failure ! */
+ pr_debug("RNG Driver: ioremap failed.\n");
+ } 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) {
+ pr_debug("RNG Driver: Error installing Interrupt Handler\n");
+ } else {
+ 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) {
+ pr_debug
+ ("RNG: Unknown or unexpected RNG type %d (FIFO size %d)."
+ " Failing driver initialization\n", 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_RNGC)
+ int count_for_reseed = 0;
+ INIT_COMPLETION(rng_seed_done);
+#endif
+#if defined(FSL_HAVE_RNGC)
+ if (RNG_RESEED()) {
+ do {
+ RNG_CLEAR_ERR();
+ RNG_SEED_GEN();
+ wait_for_completion(&rng_seed_done);
+ if (count_for_reseed == 3) {
+ os_printk
+ ("Device could 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) {
+ pr_debug("RNG: FIFO staying empty (%d)\n",
+ 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) {
+ pr_debug("RNG: FIFO empty %d times, max loop count %d\n",
+ 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();
+
+ pr_debug("RNG: entropy task starting\n");
+
+ while ((work = RNG_GET_WORK_ENTRY()) != NULL) {
+ pr_debug("RNG: found %d bytes of work at %p (%p)\n",
+ work->length, work, work->data_local);
+ 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) {
+ pr_debug("RNG: Waking queued processes\n");
+ 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 {
+ pr_debug
+ ("RNG: Callback ptr for %p is NULL\n",
+ work);
+ }
+ }
+ }
+ } /* while */
+
+ pr_debug("RNG: entropy task ending\n");
+
+ os_dev_task_return(OS_ERROR_OK_S);
+} /* rng_entropy_task */
+
+/*!***************************************************************************/
+/* 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)
+{
+ pr_debug("RNG Driver: Security Failure Alarm received.\n");
+
+ rng_cleanup();
+
+ return;
+}
+
+#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)
+{
+ pr_debug("RNG WR: 0x%4x : 0x%08x\n", 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..8619f45b615a
--- /dev/null
+++ b/drivers/mxc/security/rng/shw_driver.c
@@ -0,0 +1,1164 @@
+/*
+ * 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 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.
+ * @ingroup RNG
+ */
+
+#include "portable_os.h"
+#include "fsl_shw.h"
+
+#include "shw_internals.h"
+
+/*!****************************************************************************
+ *
+ * Function Declarations
+ *
+ *****************************************************************************/
+
+/* kernel interface functions */
+OS_DEV_INIT_DCL(shw_init);
+OS_DEV_SHUTDOWN_DCL(shw_shutdown);
+OS_DEV_IOCTL_DCL(shw_ioctl);
+
+/*!****************************************************************************
+ *
+ * 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
+ */
+
+/*!
+ * 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;
+
+#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, 1, /* api version number - major & minor */
+ 1, 0, /* 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 */
+ {0, 0, 0, 0}
+ , /* 3DES */
+ {0, 0, 0, 0} /* ARC4 */
+ }
+ ,
+};
+
+/* 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 */
+
+ pr_debug("SHW Driver: Loading\n");
+
+ 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) {
+ pr_debug("SHW Driver: Failed to setup user"
+ " i/f: %d\n", error_code);
+ }
+ }
+ /* queue_lock not NULL */
+ if (error_code != OS_ERROR_OK_S) {
+ pr_debug("SHW: Driver initialization failed. %d\n", error_code);
+ shw_cleanup();
+ } else {
+ pr_debug("SHW: Driver initialization complete.\n");
+ }
+
+ 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)
+{
+
+ pr_debug("SHW: shutdown called\n");
+ 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);
+ }
+ pr_debug("SHW Driver: Cleaned up\n");
+} /* 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();
+
+ pr_debug("SHW: IOCTL %d received\n", os_dev_get_ioctl_op());
+ switch (os_dev_get_ioctl_op()) {
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_REGISTER_USER:
+ {
+ 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:
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RESULTS:
+ code = get_results(user_ctx, (struct results_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_CAPABILITIES:
+ code = get_capabilities(user_ctx, (fsl_shw_pco_t *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_GET_RANDOM:
+ pr_debug("SHW: get_random ioctl received\n");
+ code = get_random(user_ctx, (struct get_random_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ case SHW_IOCTL_REQUEST + SHW_USER_REQ_ADD_ENTROPY:
+ pr_debug("SHW: add_entropy ioctl received\n");
+ code = add_entropy(user_ctx, (struct add_entropy_req *)
+ os_dev_get_ioctl_arg());
+ break;
+
+ default:
+ pr_debug("SHW: Unexpected ioctl %d\n", os_dev_get_ioctl_op());
+ break;
+ }
+
+ os_dev_ioctl_return(code);
+}
+
+/*!***************************************************************************/
+/* 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 uco Kernel version of uco associated with the request.
+ *
+ * @return void
+ */
+static void shw_user_callback(fsl_shw_uco_t * uco)
+{
+ pr_debug("SHW: Signalling callback user process for context %p\n", uco);
+ os_send_signal(uco->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));
+ error_code = os_driver_complete_registration(reg_handle, shw_major_node,
+ SHW_DRIVER_NAME);
+
+ if (error_code != OS_ERROR_OK_S) {
+ /* failure ! */
+ pr_debug("SHW Driver: register device driver failed: %d\n",
+ error_code);
+ } else { /* success */
+ shw_device_registered = TRUE;
+ pr_debug("SHW Driver: Major node is %d\n",
+ os_driver_get_major(reg_handle));
+ }
+
+ 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);
+ }
+ pr_debug("SHW: init uco returning %d (flags %x)\n", code,
+ user_ctx->flags);
+
+ 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));
+}
+
+/*!
+ * 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;
+ pr_debug("SHE: Received get_cap request: 0x%p/%u/0x%x\n",
+ req.capabilities, req.size, sizeof(fsl_shw_pco_t));
+ endcap = req.capabilities + 1; /* point to end of structure */
+ user_bounds = (void *)req.capabilities + req.size; /* end of area */
+
+ printk(KERN_INFO "next: %p, end: %p\n", endcap, user_bounds); //
+ /* 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;
+ }
+
+ /* 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. */
+
+ pr_debug("SHW: get capabilities returning %d\n", code);
+
+ 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);
+ pr_debug("SHW: get_random() for %d bytes in %sblocking mode\n",
+ req.size, (req.hdr.flags & FSL_UCO_BLOCKING_MODE) ?
+ "" : "non-");
+ req.hdr.code =
+ fsl_shw_get_random(user_ctx, req.size, req.random);
+
+ pr_debug("SHW: get_random() returning %d\n", req.hdr.code);
+
+ /* 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.
+ */
+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;
+
+ /* 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);
+
+ SHW_REMOVE_USER(user_ctx);
+
+ return FSL_RETURN_OK_S;
+}
+
+#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_RNGC))
+
+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;
+}
+
+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;
+}
+
+EXPORT_SYMBOL(fsl_shw_add_entropy);
+EXPORT_SYMBOL(fsl_shw_get_random);
+
+#endif
+
+#ifndef FSL_HAVE_SAHARA
+#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;
+}
+
+#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
+
+#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
+
+#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 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
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..6dd609eb378d
--- /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_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..881cee9711cd
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_auth.c
@@ -0,0 +1,792 @@
+/*
+ * 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_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
+
+/* Remove warning */
+#define SUPPORT_SSL 0
+
+
+/*! 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 auth length
+ *
+ * @return The encoded length
+ */
+#define COMPUTE_NIST_AUTH_LEN_SIZE(l) \
+({ \
+ unsigned val; \
+ if ((uint32_t)(l) < 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; \
+ } \
+}
+
+/*! Buffer to repetively sink useless CBC output */
+static uint8_t cbc_buffer[CBC_BUF_LEN];
+
+/*!
+ * 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 which will load the key and counter values into
+ * Sahara.
+ *
+ * @param[in,out] desc_chain Where to append the new descriptor
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Location of CTR value
+ * @param key_info Location of the key
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t load_ctr_key(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ fsl_shw_sko_t * key_info)
+{
+ fsl_shw_return_t status;
+
+ /* Assume AES */
+ uint32_t header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+
+ /* Assume CCM-AES for now, since that is all that is supported */
+ status = sah_add_in_key_desc(header,
+ auth_ctx->cipher_ctx_info.context,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ key_info, user_ctx->mem_util, desc_chain);
+ return status;
+}
+
+/*!
+ * Append a descriptor which will load zeros into the CBC CTX registers.
+ * It also sets the mode to CBC.
+ *
+ * @param[in,out] desc_chain Where to append the new descriptor
+ * @param user_ctx Info for acquiring memory
+ * @param encrypt 0 for decrypt, non-zero for encrypt
+ * @param size Octet count of @a in and @a out
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t load_dummy_iv(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ int encrypt, uint32_t size)
+{
+ fsl_shw_return_t status;
+ /* Desc. #1. in CBC mode. Assume /AES. */
+ uint32_t header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_mode_cbc;
+
+ if (encrypt) {
+ header ^= sah_insert_skha_encrypt;
+ }
+
+ status = sah_add_two_in_desc(header, block_zeros, size, NULL, 0,
+ user_ctx->mem_util, desc_chain);
+
+ return status;
+}
+
+/*!
+ * 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 (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);
+ }
+
+ *data_len = auth_ctx->auth_info.CCM_ctx_info.block_size_bytes +
+ auth_data_length + auth_size_length + auth_pad_length;
+
+ return status;
+}
+
+/*!
+ * Append a descriptor chain which will process the payload in
+ * CCM mode.
+ *
+ * @param[in,out] desc_chain Where to append the new descriptor
+ * @param user_ctx Info for acquiring memory
+ * @param encrypt 0 for decrypt, non-zero for encrypt
+ * @param size Length in octets of @a input and @a output
+ * @param input Location of source data
+ * @param[out] output Location to store output data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t process_payload(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ int encrypt,
+ uint32_t size,
+ const uint8_t * input,
+ uint8_t * output)
+{
+ fsl_shw_return_t status;
+ /* Assume AES */
+ uint32_t header = SAH_HDR_SKHA_SET_MODE_ENC_DEC
+ ^ sah_insert_skha_mode_ccm ^ sah_insert_skha_modulus_128;
+
+ if (encrypt) {
+ header ^= sah_insert_skha_encrypt;
+ }
+
+ status = sah_add_in_out_desc(header, input, size, output, size,
+ user_ctx->mem_util, desc_chain);
+
+ return status;
+}
+
+/*!
+ * 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
+ * @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,
+ 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;
+
+ /* 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;
+ } else {
+ uint32_t temp_buf_flag;
+
+ 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);
+ /* 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 */
+ if (status == FSL_RETURN_OK_S) {
+ status =
+ sah_Create_Link(user_ctx->mem_util, &link1,
+ (uint8_t *) auth_data,
+ auth_data_length,
+ SAH_USES_LINK_DATA);
+ /* for next/first use of temp_buf */
+ temp_buf_flag = SAH_OWNS_LINK_DATA;
+ }
+ cbc_data_length = auth_data_length;
+ } /* else not NIST */
+
+ /*
+ * Auth data links have been created. Now create link for the
+ * useless output of the CBC calculation.
+ */
+ if (status == FSL_RETURN_OK_S) {
+ 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);
+ }
+ 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);
+ cbc_data_length -= linklen;
+ }
+ }
+
+ 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);
+ }
+ } else {
+ /* Header to set up crank through auth data */
+ status = sah_Append_Desc(user_ctx->mem_util, desc_chain,
+ header, link1, link2);
+ }
+ }
+
+ return status;
+}
+
+/*!
+ * Append a descriptor chain which will pull the MAC (CBC IV) out of the
+ * hardware registers.
+ *
+ * @param[in,out] desc_chain Where to append the new descriptor
+ * @param user_ctx Info for acquiring memory
+ * @param size Length in octets of desired MAC
+ * @param[out] mac Location to store the MAC
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t extract_mac(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ uint32_t size, uint8_t * mac)
+{
+ fsl_shw_return_t status;
+ uint32_t header = SAH_HDR_SKHA_READ_CONTEXT_IV;
+
+ /* Pull MAC out of IV register */
+ status = sah_add_in_out_desc(header, NULL, 0, mac, size,
+ user_ctx->mem_util, desc_chain);
+
+ return status;
+}
+
+/*!
+ * Append a descriptor chain which will (encrypt) the MAC with CTR0.
+ * Since CTR mode works both ways, the input could be an encrypted
+ * MAC with the output being the decrypted version.
+ *
+ * @param[in,out] desc_chain Where to append the new descriptor
+ * @param user_ctx Info for acquiring memory
+ * @param auth_ctx Info for CTR0, size of MAC
+ * @param input Location of MAC
+ * @param output Location to store (encrypted) MAC.
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+static inline fsl_shw_return_t encrypt_mac(sah_Head_Desc ** desc_chain,
+ fsl_shw_uco_t * user_ctx,
+ fsl_shw_acco_t * auth_ctx,
+ const uint8_t * input,
+ uint8_t * output)
+{
+ fsl_shw_return_t status;
+ /* Assuming AES here... */
+ uint32_t header = SAH_HDR_SKHA_SET_MODE_IV_KEY
+ ^ sah_insert_skha_encrypt
+ ^ sah_insert_skha_mode_ctr ^ sah_insert_skha_modulus_128;
+
+ /* Load CTR0 back in */
+ status = sah_add_two_in_desc(header,
+ auth_ctx->cipher_ctx_info.context,
+ auth_ctx->cipher_ctx_info.block_size_bytes,
+ NULL, 0, user_ctx->mem_util, desc_chain);
+
+ if (status == FSL_RETURN_OK_S) {
+ /* and encrypt the input as the auth value */
+ header = SAH_HDR_SKHA_ENC_DEC; /* Desc. #4 SKHA Enc/Dec */
+
+ status = sah_add_in_out_desc(header,
+ input, auth_ctx->mac_length,
+ output, auth_ctx->mac_length,
+ user_ctx->mem_util, desc_chain);
+ }
+
+ return status;
+}
+
+#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
+ return ret;
+ }
+
+ 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;
+ }
+ ret = load_ctr_key(&desc_chain, user_ctx, auth_ctx, cipher_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ header = SAH_HDR_SKHA_ENC_DEC;
+ DESC_IN_OUT(header, auth_ctx->cipher_ctx_info.block_size_bytes,
+ garbage_output, auth_ctx->cipher_ctx_info.block_size_bytes,
+ garbage_output);
+
+#ifndef NO_ZERO_IV_LOAD
+ ret = load_dummy_iv(&desc_chain, user_ctx,
+ 1,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+#endif
+
+ if (auth_data_length > 0) {
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+ /* if auth_data_length > 0 */
+ ret = process_payload(&desc_chain, user_ctx, 1,
+ payload_length, payload, ct);
+
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Pull out the CBC-MAC value. */
+ ret = extract_mac(&desc_chain, user_ctx,
+ auth_ctx->mac_length, auth_ctx->unencrypted_mac);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* Now load CTR0 in, and encrypt the MAC */
+ ret = encrypt_mac(&desc_chain, user_ctx, auth_ctx,
+ auth_ctx->unencrypted_mac, auth_value);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ 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;
+ uint8_t *calced_auth = NULL;
+ unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE;
+
+ SAH_SF_USER_CHECK();
+
+ /* Only support INIT and FINALIZE flags right now. */
+ if (auth_ctx->mode != FSL_ACC_MODE_CCM) {
+ ret = FSL_RETURN_BAD_MODE_S;
+ goto out;
+ }
+ 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;
+ }
+ ret = load_ctr_key(&desc_chain, user_ctx, auth_ctx, cipher_key_info);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* 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);
+
+#ifndef NO_ZERO_IV_LOAD
+ ret = load_dummy_iv(&desc_chain, user_ctx, 1,
+ auth_ctx->auth_info.CCM_ctx_info.block_size_bytes);
+#endif
+
+ if (auth_data_length > 0) {
+ ret = add_assoc_preamble(&desc_chain, user_ctx,
+ auth_ctx, auth_data, auth_data_length);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ }
+ /* if auth_data_length > 0 */
+ ret = process_payload(&desc_chain, user_ctx, 0,
+ payload_length, ct, payload);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ /* 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);
+ ret = extract_mac(&desc_chain, user_ctx,
+ auth_ctx->mac_length, calced_auth);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ 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;
+ }
+
+ SAH_SF_EXECUTE();
+
+ 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;
+ }
+ }
+ }
+
+ out:
+ SAH_SF_DESC_CLEAN();
+ DESC_TEMP_FREE(calced_auth);
+
+ (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_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..fc56e4a9e33e
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_user.c
@@ -0,0 +1,158 @@
+/*
+ * 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_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__ */
+
+/*! This matches Sahara2 capabilities... */
+fsl_shw_pco_t sahara2_capabilities = {
+ 1, 1, /* api version number - major & minor */
+ 1, 2, /* 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 */
+ }
+};
+
+/* 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)
+{
+ /*
+ * Need to get the driver/hardware versions populated...
+ * which is why the user_ctx is here.
+ */
+ user_ctx = 0;
+ return &sahara2_capabilities;
+}
+
+/* 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..28c5f013dc5c
--- /dev/null
+++ b/drivers/mxc/security/sahara2/fsl_shw_wrap.c
@@ -0,0 +1,732 @@
+/*
+ * 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_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>sha1</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>sha1</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>sha1</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>sha1</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 "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);
+#endif
+
+#define ICV_LENGTH 16
+#define T_LENGTH 16
+#define KEK_LENGTH 16
+#define LENGTH_LENGTH 1
+#define ALGORITHM_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 KEY_PRIME_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH)
+
+/*
+ * 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_sha1
+ ^ 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;
+
+ /* Compute T = SLID_decrypt(T'); leave in RED slot */
+ ret = do_scc_slot_decrypt(user_ctx, 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]) {
+ ret = FSL_RETURN_AUTH_FAILED_S;
+ goto out;
+ }
+ }
+
+ /* This is no longer needed. */
+ DESC_TEMP_FREE(hmac);
+
+ /* Compute KEK = SHA1(T | ownerid). Rewrite slot with value */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm_sha1 ^ 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;
+
+ ret = do_scc_slot_alloc(user_ctx, T_LENGTH, key_info->userid,
+ &T_key_info.handle);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+
+ ret = do_scc_slot_alloc(user_ctx, 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
+ (void)do_scc_slot_dealloc(user_ctx, 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
+ ret = do_scc_slot_load_slot(user_ctx, 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 = SHA1(T | Ownerid) */
+ header = (SAH_HDR_MDHA_SET_MODE_HASH /* #8 */
+ ^ sah_insert_mdha_init
+ ^ sah_insert_mdha_algorithm[FSL_HASH_ALG_SHA1]
+ ^ 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);
+
+ /* 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 */
+ ret = do_scc_slot_encrypt(user_ctx, 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) {
+ do_scc_slot_dealloc(user_ctx, key_info->userid,
+ T_key_info.handle);
+ do_scc_slot_dealloc(user_ctx, 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 */
+
+ /* 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();
+
+ ret =
+ do_scc_slot_alloc(user_ctx, key_info->key_length, key_info->userid,
+ &key_info->handle);
+ if (ret != FSL_RETURN_OK_S) {
+ goto out;
+ }
+ slot_allocated = 1;
+
+ key_info->flags |= FSL_SKO_KEY_ESTABLISHED;
+ switch (establish_type) {
+ case FSL_KEY_WRAP_CREATE:
+ /* 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 */
+#ifdef DIAG_SECURITY_FUNC
+ LOG_DIAG("Creating random key");
+#endif
+ SAH_SF_EXECUTE();
+ /* Restore user's old flag value */
+ user_ctx->flags = old_flags;
+ break;
+
+ case FSL_KEY_WRAP_ACCEPT:
+ 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 */
+ ret = do_scc_slot_load_slot(user_ctx, key_info->userid,
+ key_info->handle, key,
+ rounded_key_length);
+ break;
+
+ case FSL_KEY_WRAP_UNWRAP:
+ /* 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;
+ scc_err = do_scc_slot_dealloc(user_ctx, 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;
+ }
+
+ /* Need to deallocate on successful extraction */
+ do_scc_slot_dealloc(user_ctx, 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) {
+ ret = do_scc_slot_dealloc(user_ctx, key_info->userid,
+ key_info->handle);
+ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED |
+ FSL_SKO_KEY_PRESENT);
+ }
+
+ 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..2b39805d72f1
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/adaptor.h
@@ -0,0 +1,95 @@
+/*
+ * 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 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_info {
+ 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;
+
+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 do_scc_slot_alloc(fsl_shw_uco_t * user_ctx, uint32_t key_len,
+ uint64_t ownerid, uint32_t * slot);
+fsl_shw_return_t do_scc_slot_dealloc(fsl_shw_uco_t * user_ctx, uint64_t ownerid,
+ uint32_t slot);
+fsl_shw_return_t do_scc_slot_load_slot(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid, uint32_t slot,
+ const uint8_t * key,
+ uint32_t key_length);
+fsl_shw_return_t do_scc_slot_encrypt(fsl_shw_uco_t * user_ctx, uint64_t ownerid,
+ uint32_t slot, uint32_t key_length,
+ uint8_t * black_data);
+
+fsl_shw_return_t do_scc_slot_decrypt(fsl_shw_uco_t * user_ctx, uint64_t ownerid,
+ uint32_t slot, uint32_t key_length,
+ const uint8_t * black_data);
+
+#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..cbd15357b754
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/diagnostic.h
@@ -0,0 +1,78 @@
+/*
+ * 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 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
+
+/*!
+********************************************************************
+* @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) \
+({ \
+ char* fname = strrchr(__FILE__, '/'); \
+ \
+ sah_Log_Diag (fname ? fname+1 : __FILE__, __LINE__, diag); \
+})
+
+#ifndef __KERNEL__
+void sah_Log_Diag(char *source_name, int source_line, char *diag);
+#endif
+#endif
+
+#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 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)
+#define LOG_KDIAG(diag) \
+ os_printk (KERN_ALERT "sahara (%s:%i): %s\n", \
+ strrchr(__FILE__, '/')+1, __LINE__, diag);
+
+#define sah_Log_Diag(n, l, d) \
+ os_printk("%s:%i: %s\n", n, l, d)
+#endif
+
+#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..1673b8ce6a1d
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_platform.h
@@ -0,0 +1,115 @@
+/*
+ * 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_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_MX27)
+
+#define FSL_HAVE_SAHARA2
+#define SUBMIT_MULTIPLE_DARS
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define USE_OLD_PTRS
+
+#elif defined(CONFIG_ARCH_MXC91131)
+
+#define FSL_HAVE_SCC
+#define FSL_HAVE_RNGA
+#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 USE_OLD_PTRS
+#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_MXC91321)
+
+#define FSL_HAVE_SAHARA2
+#define FSL_HAVE_RTIC
+#define FSL_HAVE_SCC
+#define NO_OUTPUT_1K_CROSSING
+#define USE_OLD_PTRS
+
+#elif defined(CONFIG_ARCH_MXC92323)
+
+#define FSL_HAVE_SCC2
+#define FSL_HAVE_SAHARA4
+#define FSL_HAVE_RTIC2
+#define NO_1K_CROSSING
+#define NO_RESEED_WORKAROUND
+#define NEED_CTR_WORKAROUND
+#define USE_3WORD_BURST
+
+#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..ef3311d90abc
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/fsl_shw.h
@@ -0,0 +1,1964 @@
+/*
+ * 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
+ */
+
+/*
+ * 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 use te 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.
+ *
+ *
+ * @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 Capababilities
+ *
+ * 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 generaator.
+ *
+ *
+ * @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 Wrapped Keys
+ *
+ * On platforms with a Secure Memory, the function #fsl_shw_establish_key() can
+ * be used to place a key into the Secure Memory. This key then be used
+ * directly by the cryptographic hardware. It later then be wrapped
+ * (cryptographically obscured) by #fsl_shw_extract_key() and stored for later
+ * use.
+ *
+ * The wrapping and unwrapping functions provide security against unauthorized
+ * use and detection of tampering.
+ */
+
+/*! @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 peformed 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_RNGC)
+
+#include "rng_driver.h"
+
+#else
+
+#error FSL_SHW_API_platform_not_recognized
+
+#endif
+
+#endif /* HAVE_SAHARA2 */
+
+#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 maninpulated 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 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 {
+ FSL_UCO_BLOCKING_MODE, /*!< 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_CALLBACK_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_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 {
+ FSL_RETURN_OK_S = 0, /*!< 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_ERROR_S, /*!< Failure for non-specific reason. */
+ FSL_RETURN_NO_RESOURCE_S, /*!< Operation failed because some resource was
+ not able to be allocated. */
+ FSL_RETURN_BAD_ALGORITHM_S, /*!< Crypto algorithm unrecognized or
+ improper. */
+ FSL_RETURN_BAD_MODE_S, /*!< Crypto mode unrecognized or improper. */
+ FSL_RETURN_BAD_FLAG_S, /*!< Flag setting unrecognized or
+ inconsistent. */
+ FSL_RETURN_BAD_KEY_LENGTH_S, /*!< Improper or unsupported key length for
+ algorithm. */
+ FSL_RETURN_BAD_KEY_PARITY_S, /*!< Improper parity in a (DES, TDES) key. */
+ FSL_RETURN_BAD_DATA_LENGTH_S, /*!< Improper or unsupported data length for
+ algorithm or internal buffer. */
+ FSL_RETURN_AUTH_FAILED_S, /*!< Authentication failed in
+ authenticate-decrypt operation. */
+ FSL_RETURN_MEMORY_ERROR_S, /*!< A memory error occurred. */
+ FSL_RETURN_INTERNAL_ERROR_S /*!< An error internal to the hardware
+ occurred. */
+} 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 {
+ 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 {
+ /*!
+ * 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 {
+ 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 {
+ /*!
+ * 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
+} 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 {
+ 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 {
+ 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 {
+ 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 {
+ /*!
+ * 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 {
+ 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_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 {
+ 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;
+
+/*!
+ * 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 {
+ 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;
+
+ /*! @} *//* 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 {
+} 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 { /* 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 { /* fsl_shw_result */
+} fsl_shw_result_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 { /* 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 { /* 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 { /* 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 { /* 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 { /* 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 { /* 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 Capababilities 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 Capababilities 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 Capababilities 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 Capababilities 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 Capababilities 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 Capababilities 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 Capababilities 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 Capababilities 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);
+
+ /*! @} *//* 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 oustanding.
+ *
+ *
+ * @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);
+
+ /*! @} *//* 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 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);
+
+/*!
+ * 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 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 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 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 paramater @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 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 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);
+
+ /*! @} *//* 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 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-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);
+
+/*!
+ * 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);
+
+/*!
+ * 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);
+
+ /*! @} *//* 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
+ */
+
+#endif /* API_DOC */
+
+#endif /* FSL_SHW_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..66b5d9ec0295
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/linux_port.h
@@ -0,0 +1,1705 @@
+/*
+ * 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 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 */
+
+#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
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
+#include <asm/arch/clock.h> /* clock en/disable for DPM */
+#else
+#include <linux/clk.h> /* clock en/disable for DPM */
+#endif
+
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h> /* ioremap() */
+#include <asm/irq.h>
+#include <asm/uaccess.h> /* copy_to_user(), copy_from_user() */
+#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 = -EFAULT /*!< Bad address */
+} 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
+ struct class_device *cd; /*!< Result of class_simple_device_add() */
+ 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.
+ */
+typedef int (*os_interrupt_handler_t) (int, void *);
+
+/*!
+ * @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, sizeof(*handle), 0);
+ 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) {
+ code =
+ register_chrdev(major, driver_name, &handle->fops);
+ 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 {
+ handle->cd =
+ class_device_create(handle->cs,
+ NULL,
+ handle->dev,
+ NULL,
+ driver_name);
+ if (IS_ERR(handle->cd)) {
+ class_device_destroy(handle->cs,
+ handle->
+ dev);
+ 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) {
+ class_device_destroy(handle->cs, handle->dev);
+ 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)
+ */
+/* This may be a GCC-ism which needs to be ported to ANSI */
+#define os_printk(msg, s...) \
+ (void) printk(msg, ## s)
+
+/*!
+ * 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)
+
+/*!
+ * 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)
+
+/*!
+ * 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 failedn");
+ }
+
+ 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 failedn");
+ }
+
+ 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 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) \
+static ssize_t function_name(struct file* file_p_, char* user_buffer_, \
+ size_t count_bytes_, loff_t* file_position_)
+
+/*!
+ * 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, 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) \
+static irqreturn_t function_name(int N1_, void* N2_)
+
+/*!
+ * 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.
+ *
+ */
+#define os_dev_isr_return(code) \
+do { \
+ /* Unused warnings */ \
+ (void)N1_; \
+ (void)N2_; \
+ \
+ return IRQ_RETVAL(code); \
+} while (0)
+
+/*!
+ * 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)
+ /*! @} *//* 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
+ */
+#define os_cache_clean_range(start,len) \
+{ \
+ dmac_clean_range(start, (void *)((unsigned long)(start) + len)); \
+ outer_clean_range(__pa(start), __pa((unsigned long)(start) + len)); \
+}
+
+/*!
+ * Invalidate a range of addresses in the cache
+ *
+ * @param start Starting virtual address
+ * @param len Number of bytes to flush
+ *
+ * @return void
+ */
+#define os_cache_inv_range(start,len) \
+{ \
+ dmac_inv_range(start, (void *)((unsigned long)(start) + len)); \
+ outer_inv_range(__pa(start), __pa((unsigned long)(start) + len)); \
+}
+
+/*!
+ * 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
+ */
+#define os_cache_flush_range(start,len) \
+{ \
+ dmac_flush_range(start, (void *)((unsigned long)(start) + len)); \
+ outer_flush_range(__pa(start), __pa((unsigned long)(start) + len)); \
+}
+
+ /*! @} *//* 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..6211e119531b
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/portable_os.h
@@ -0,0 +1,1420 @@
+/*
+ * 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 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_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 /*!< Bad address */
+} 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_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);
+
+ /*! @} *//* 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);
+
+#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..a31eefe96a54
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_driver_common.h
@@ -0,0 +1,98 @@
+/*
+ * 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_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 indictes 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;
+
+
+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..a03e53999c4a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_hardware_interface.h
@@ -0,0 +1,93 @@
+/*
+ * 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_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);
+
+#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..1f2d072f27ac
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_kernel.h
@@ -0,0 +1,89 @@
+/*
+ * 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_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_MX33) \
+ || defined(CONFIG_ARCH_MXC92323)
+#include <asm/arch/hardware.h>
+#define SAHA_BASE_ADDR SAHARA_BASE_ADDR
+#define SAHARA_IRQ INT_SAHARA
+#else
+#include <asm/arch/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.
+ */
+#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 */
+#define SAHARA_SCC_ALLOC _IOWR(SAH_IOC_MAGIC, 8, scc_slot_t)
+#define SAHARA_SCC_DEALLOC _IOWR(SAH_IOC_MAGIC, 9, scc_slot_t)
+#define SAHARA_SCC_LOAD _IOWR(SAH_IOC_MAGIC, 10, scc_slot_t)
+#define SAHARA_SCC_UNLOAD _IOWR(SAH_IOC_MAGIC, 11, scc_slot_t)
+#define SAHARA_SCC_SLOT_ENC _IOWR(SAH_IOC_MAGIC, 12, scc_slot_t)
+#define SAHARA_SCC_SLOT_DEC _IOWR(SAH_IOC_MAGIC, 13, 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..3bca7215b359
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sah_memory_mapper.h
@@ -0,0 +1,82 @@
+/*
+ * 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_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 (
+ sah_Head_Desc *desc);
+
+sah_Link *sah_Copy_Links (
+ 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 (
+ 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);
+
+
+#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..b7681e0c34ec
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sahara.h
@@ -0,0 +1,2184 @@
+/*
+ * 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 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
+ *
+ */
+
+#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
+/*! @} */
+
+/*!
+*******************************************************************************
+* 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 {
+ /*!
+ * 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 = 1,
+ /*!
+ * 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 = 2,
+ /*! Do not free descriptor chain after driver (adaptor) finishes */
+ FSL_UCO_SAVE_DESC_CHAIN = 4,
+ /*!
+ * User has made at least one request with callbacks requested, so API is
+ * ready to handle others.
+ */
+ FSL_UCO_CALLBACK_SETUP_COMPLETE = 8,
+ /*!
+ * (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 = 16,
+} 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_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 {
+ /*! 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 {
+ /*!
+ * Context is empty. Hash is started from scratch, with a
+ * message-processed count of zero.
+ */
+ FSL_HASH_FLAGS_INIT = 1,
+ /*!
+ * 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 = 2,
+ /*! Place context into hardware before hashing. */
+ FSL_HASH_FLAGS_LOAD = 4,
+ /*!
+ * PAD message and perform final digest operation. If user message is
+ * pre-padded, this flag should not be used.
+ */
+ FSL_HASH_FLAGS_FINALIZE = 8,
+} 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,
+} 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 {
+ 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;
+
+/******************************************************************************
+ * 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 current_dar; /*!< 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 {
+} 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;
+
+/* 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 */
+#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;
+
+/* 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];
+} 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 */
+} 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;
+
+typedef struct fsl_shw_dsa_cmn_t {
+ unsigned p_size; /*!< byte count of P (prime modulus) and G (generator) */
+ unsigned q_size; /*!< byte count of Q (divisor) */
+ uint8_t data[]; /*!< P, Q, G */
+} fsl_shw_dsa_cmn_t;
+
+/*! Get size in bytes of P field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_P_SIZE(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->p_size; \
+})
+
+/*! Get pointer to P field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_P(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->data; \
+})
+
+/*! Get size in bytes of Q field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_Q_SIZE(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->q_size; \
+})
+
+/*! Get pointer to Q field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_Q(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size; \
+})
+
+/*! Get size in bytes of G field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_G_SIZE(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->p_size; \
+})
+
+/*! Get pointer to G field from pointer to struct fsl_shw_dsa_cmn_t */
+#define DSA_CMN_GET_G(parm) \
+({ \
+ struct fsl_shw_dsa_cmn_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->q_size; \
+})
+
+typedef struct fsl_shw_dsa_pub_t {
+ unsigned y_size; /*!< byte count of y field */
+ uint8_t data[]; /*!< y */
+} fsl_shw_dsa_pub_t;
+
+/*! Get size in bytes of Y field from pointer to struct fsl_shw_dsa_pub_t */
+#define DSA_PUB_GET_Y_SIZE(parm) \
+({ \
+ struct fsl_shw_dsa_pub_t* parmp = (parm); \
+ \
+ parmp->y_size; \
+})
+
+/*! Get pointer to P field from pointer to struct fsl_shw_dsa_pub_t */
+#define DSA_PUB_GET_Y(parm) \
+({ \
+ struct fsl_shw_dsa_pub_t* parmp = (parm); \
+ \
+ parmp->data; \
+})
+
+typedef struct fsl_shw_curve_fp_t {
+ unsigned p_size; /*!< byte count of mod */
+ unsigned r_size; /*!< byte count of r */
+ unsigned a_size; /*!< byte count of a */
+ unsigned b_size; /*!< byte count of b */
+ unsigned c_size; /*!< byte count of c */
+ unsigned Gx_size; /*!< byte count of Gx */
+ unsigned Gy_size; /*!< byte count of Gy */
+ uint8_t data[]; /*!< p, r, a, b , Gx, Gy, (k),(SEED, c) */
+} fsl_shw_curve_fp_t;
+
+/*! Get size in bytes of P field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_P_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->p_size; \
+})
+
+/*! Get pointer to P field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_P(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data; \
+})
+
+/*! Get size in bytes of R field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_R_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->r_size; \
+})
+
+/*! Get pointer to R field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_R(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size; \
+})
+
+/*! Get size in bytes of A field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_A_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->a_size; \
+})
+
+/*! Get pointer to A field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_A(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size; \
+})
+
+/*! Get size in bytes of B field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_B_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->b_size; \
+})
+
+/*! Get pointer to B field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_B(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size; \
+})
+
+/*! Get size in bytes of Gx field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_GX_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->Gx_size; \
+})
+
+/*! Get pointer to Gx field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_GX(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size \
+ + parmp->b_size; \
+})
+
+/*! Get size in bytes of Gy field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_GY_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->Gx_size; \
+})
+
+/*! Get pointer to Gy field from pointer to struct fsl_shw_curve_fp_t */
+#define FP_CRV_GET_GY(parm) \
+({ \
+ struct fsl_shw_curve_fp_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size \
+ + parmp->b_size + parmp->Gx_size; \
+})
+
+typedef struct fsl_shw_curve_f2m_t {
+ unsigned p_size; /*!< byte count of polynomial */
+ unsigned r_size; /*!< byte count of r */
+ unsigned a_size; /*!< byte count of a */
+ unsigned c_size; /*!< byte count of c */
+ unsigned Gx_size; /*!< byte count of Gx */
+ unsigned Gy_size; /*!< byte count of Gy */
+ unsigned b_size; /*!< byte count of b */
+ uint8_t data[]; /* poly, r, a, b, Gx, Gy, b */
+} fsl_shw_curve_f2m_t;
+
+/*! Get size in bytes of P field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_P_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->p_size; \
+})
+
+/*! Get pointer to P field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_P(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data; \
+})
+
+/*! Get size in bytes of R field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_R_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->r_size; \
+})
+
+/*! Get pointer to R field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_R(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size; \
+})
+
+/*! Get size in bytes of A field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_A_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->a_size; \
+})
+
+/*! Get pointer to A field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_A(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size; \
+})
+
+/*! Get size in bytes of C field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_C_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->c_size; \
+})
+
+/*! Get pointer to C field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_C(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size; \
+})
+
+/*! Get size in bytes of Gx field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_GX_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->Gx_size; \
+})
+
+/*! Get pointer to Gx field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_GX(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size \
+ + parmp->c_size; \
+})
+
+/*! Get size in bytes of Gy field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_GY_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->Gy_size; \
+})
+
+/*! Get pointer to Gy field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_GY(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size \
+ + parmp->c_size + parmp->Gx_size; \
+})
+
+/*! Get size in bytes of b field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_B_SIZE(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->b_size; \
+})
+
+/*! Get pointer to b field from pointer to struct fsl_shw_curve_f2m_t */
+#define F2M_CRV_GET_B(parm) \
+({ \
+ struct fsl_shw_curve_f2m_t* parmp = (parm); \
+ \
+ parmp->data + parmp->p_size + parmp->r_size + parmp->a_size \
+ + parmp->c_size + parmp->Gx_size + parmp->Gy_size; \
+})
+
+typedef struct fsl_shw_ecc_point_t {
+ unsigned x_size;
+ unsigned y_size;
+ uint8_t data[]; /* x, y */
+} fsl_shw_ecc_point_t;
+
+/*! Get pointer to x field from pointer to struct fsl_shw_ecc_point_t */
+#define POINT_GET_X(parm) \
+({ \
+ struct fsl_shw_ecc_point_t* parmp = (parm); \
+ \
+ parmp->data; \
+})
+
+/*! Get pointer to x field from pointer to struct fsl_shw_ecc_point_t */
+#define POINT_GET_X_SIZE(parm) \
+({ \
+ struct fsl_shw_ecc_point_t* parmp = (parm); \
+ \
+ parmp->x_size; \
+})
+
+/*! Get pointer to y field from pointer to struct fsl_shw_ecc_point_t */
+#define POINT_GET_Y(parm) \
+({ \
+ struct fsl_shw_ecc_point_t* parmp = (parm); \
+ \
+ parmp->data + parmp->y_size; \
+})
+
+/*! Get pointer to x field from pointer to struct fsl_shw_ecc_point_t */
+#define POINT_GET_Y_SIZE(parm) \
+({ \
+ struct fsl_shw_ecc_point_t* parmp = (parm); \
+ \
+ parmp->y_size; \
+})
+
+/*!
+ * 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;
+
+/******************************************************************************
+ * 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
+
+/*!
+ * 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.
+ */
+#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; \
+}
+
+/*!
+ * 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; \
+}
+
+/*!
+ * 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)
+
+/*!
+ * 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
+
+/*!
+ * 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) \
+{ \
+ if ((wkeyinfo)->key_length > 32) { \
+ *(wkeylen) = 0; \
+ } else { \
+ *(wkeylen) = 66; \
+ } \
+}
+
+/*!
+ * 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);
+
+/* 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);
+
+#if 0
+sah_Test_Status sah_Check_Test_Mode(void);
+#endif
+
+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..710c94fab896
--- /dev/null
+++ b/drivers/mxc/security/sahara2/include/sf_util.h
@@ -0,0 +1,426 @@
+/*
+ * 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 sf_util.h
+*
+* @brief Header for Sahara Descriptor-chain building Functions
+*/
+#ifndef SF_UTIL_H
+#define SF_UTIL_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 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
+
+#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_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 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_ptmult 0x00000006
+#define sah_insert_pkha_rtn_ec_f2m_aff_ptmult 0x80000007
+#define sah_insert_pkha_rtn_ec_fp_proj_ptmult 0x80000008
+#define sah_insert_pkha_rtn_ec_f2m_proj_ptmult 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_r2 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
+/*! @} */
+
+/*! 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 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, 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..89bb6a778163
--- /dev/null
+++ b/drivers/mxc/security/sahara2/km_adaptor.c
@@ -0,0 +1,630 @@
+/*
+ * 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 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>
+#ifdef FSL_HAVE_SCC
+#include <asm/arch/mxc_scc_driver.h>
+#else
+#include <asm/arch/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(do_scc_slot_alloc);
+EXPORT_SYMBOL(do_scc_slot_dealloc);
+EXPORT_SYMBOL(do_scc_slot_load_slot);
+EXPORT_SYMBOL(do_scc_slot_encrypt);
+EXPORT_SYMBOL(do_scc_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);
+
+#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
+};
+
+/*!
+ * 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;
+}
+
+/*!
+ * Allocates a slot in the SCC
+ *
+ * @brief Allocates a slot in the SCC
+ *
+ * @param user_ctx
+ * @param key_len
+ * @param ownerid
+ * @param slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t do_scc_slot_alloc(fsl_shw_uco_t * user_ctx,
+ uint32_t key_len,
+ uint64_t ownerid, uint32_t * slot)
+{
+ scc_return_t scc_status = scc_alloc_slot(key_len, ownerid, slot);
+ fsl_shw_return_t ret;
+
+ if (scc_status == SCC_RET_OK) {
+ ret = FSL_RETURN_OK_S;
+ } else {
+ ret = FSL_RETURN_NO_RESOURCE_S;
+ }
+
+ user_ctx = NULL;
+ return ret;
+}
+
+/*!
+ * Deallocates a slot in the SCC
+ *
+ * @brief Deallocates a slot in the SCC
+ *
+ * @param user_ctx
+ * @param ownerid
+ * @param slot
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t do_scc_slot_dealloc(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid, uint32_t slot)
+{
+ scc_return_t scc_status = scc_dealloc_slot(ownerid, slot);
+ user_ctx = NULL;
+ return (scc_status ==
+ SCC_RET_OK) ? FSL_RETURN_OK_S : FSL_RETURN_ERROR_S;
+}
+
+/*!
+ * Populate a slot in the SCC
+ *
+ * @brief Deallocates a slot in the SCC
+ *
+ * @param user_ctx
+ * @param uint32_t slot
+ * @param key
+ * @param key_length
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t do_scc_slot_load_slot(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid, uint32_t slot,
+ const uint8_t * key, uint32_t key_length)
+{
+ scc_return_t scc_status = scc_load_slot(ownerid, slot, (void *)key,
+ key_length);
+ user_ctx = NULL;
+ return (scc_status ==
+ SCC_RET_OK) ? FSL_RETURN_OK_S : FSL_RETURN_ERROR_S;
+}
+
+/*!
+ * Encrypt what's in a slot on the SCC
+ *
+ * @brief Encrypt what's in a slot on the SCC
+ *
+ * @param user_ctx
+ * @param ownerid
+ * @param slot
+ * @param key_length
+ * @param black_data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t do_scc_slot_encrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length, uint8_t * black_data)
+{
+ scc_return_t scc_code;
+
+ user_ctx = NULL; /* for unused-param warning */
+ scc_code = scc_encrypt_slot(ownerid, slot, key_length, black_data);
+ return (scc_code == SCC_RET_OK) ? FSL_RETURN_OK_S : FSL_RETURN_ERROR_S;
+}
+
+/*!
+ * Deallocates a slot in the SCC
+ *
+ * @brief Deallocates a slot in the SCC
+ *
+ * @param user_ctx
+ * @param ownerid
+ * @param slot
+ * @param key_length
+ * @param black_data
+ *
+ * @return A return code of type #fsl_shw_return_t.
+ */
+fsl_shw_return_t do_scc_slot_decrypt(fsl_shw_uco_t * user_ctx,
+ uint64_t ownerid,
+ uint32_t slot,
+ uint32_t key_length,
+ const uint8_t * black_data)
+{
+ scc_return_t scc_code;
+
+ user_ctx = NULL; /* for unused-param warning */
+ scc_code = scc_decrypt_slot(ownerid, slot, key_length, black_data);
+ return (scc_code == SCC_RET_OK) ? FSL_RETURN_OK_S : FSL_RETURN_ERROR_S;
+}
+
+#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..aacb8d1cb888
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_driver_interface.c
@@ -0,0 +1,1325 @@
+/*
+ * 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_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 <adaptor.h>
+#ifdef FSL_HAVE_SCC
+#include <asm/arch/mxc_scc_driver.h>
+#else
+#include <asm/arch/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 <asm/arch/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);
+
+static void sah_user_callback(fsl_shw_uco_t * uco);
+static os_error_code sah_handle_scc_slot_alloc(uint32_t info);
+static os_error_code sah_handle_scc_slot_dealloc(uint32_t info);
+static os_error_code sah_handle_scc_slot_load(uint32_t info);
+static os_error_code sah_handle_scc_slot_decrypt(uint32_t info);
+static os_error_code sah_handle_scc_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
+
+/*!
+ * 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
+
+/*!
+*******************************************************************************
+* 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;
+#ifdef DIAG_DRV_IF
+ char err_string[200];
+#endif
+
+ 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
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (clk != ERR_PTR(ENOENT)) {
+ clk_enable(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 == 0) {
+ sah_hw_version = sah_HW_Read_Version();
+ 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) {
+ printk
+ ("Sahara HW Version was not expected value.\n");
+ os_error_code = OS_ERROR_FAIL_S;
+ }
+ }
+ }
+
+ if (os_error_code == 0) {
+#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 == 0) {
+#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 == 0) {
+#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 == 0) {
+#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 == 0) {
+#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 == 0) {
+ /* set up dynamic power management (dmp) */
+ os_error_code = sah_dpm_init();
+ }
+#endif
+
+ if (os_error_code == 0) {
+ 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_error_code =
+ os_driver_complete_registration(reg_handle, Major,
+ "sahara");
+
+ if (os_error_code < 0) {
+#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 != 0) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ cleanup_module();
+#else
+ sah_cleanup();
+#endif
+ }
+#ifdef DIAG_DRV_IF
+ else {
+ sprintf(err_string, "Sahara major node is %d\n", Major);
+ LOG_KDIAG(err_string);
+ }
+#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)
+{
+ /* Unregister the device */
+#if defined(CONFIG_DEVFS_FS) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ devfs_unregister(Sahara_devfs_handle);
+#else
+ int ret_val = 0;
+
+ 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
+ {
+ struct clk *clk = clk_get(NULL, "sahara_clk");
+ if (clk != ERR_PTR(ENOENT)) {
+ clk_disable(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_ALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_ALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_scc_slot_alloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SCC_DEALLOC:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_DEALLOC IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_scc_slot_dealloc(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SCC_LOAD:
+#ifdef DIAG_DRV_IF
+ LOG_KDIAG("SAHARA_SCC_LOAD IOCTL.");
+#endif /* DIAG_DRV_IF */
+ status = sah_handle_scc_slot_load(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SCC_SLOT_DEC:
+ status = sah_handle_scc_slot_decrypt(os_dev_get_ioctl_arg());
+ break;
+
+ case SAHARA_SCC_SLOT_ENC:
+ status = sah_handle_scc_slot_encrypt(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);
+}
+
+/*!
+*******************************************************************************
+* Allocates a slot in the SCC
+*
+* @brief Allocates a slot in the SCC
+*
+* @param info slot information
+*
+* @return 0 if pass; non-zero on error
+*/
+static os_error_code sah_handle_scc_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 =
+ scc_alloc_slot(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;
+ }
+
+ /* 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)scc_dealloc_slot(slot_info.ownerid,
+ slot_info.slot);
+ }
+ }
+
+ return os_err;
+}
+
+/*!
+ * Deallocate a slot in the SCC
+ *
+ * @brief Deallocate a slot in the SCC
+ *
+ * @param info slot information
+ *
+ * @return 0 if pass; non-zero on error
+ */
+static os_error_code sah_handle_scc_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 = scc_dealloc_slot(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;
+}
+
+/*!
+ * Populate a slot in the SCC
+ *
+ * @brief Populate a slot in the SCC
+ *
+ * @param info slot information
+ *
+ * @return 0 if pass; non-zero on error
+ */
+static os_error_code sah_handle_scc_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;
+ 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) {
+ /* 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_OK_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);
+ }
+ scc_ret = scc_load_slot(slot_info.ownerid, slot_info.slot, key,
+ key_length);
+ 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;
+}
+
+/*!
+ * Decrypt data into a slot in the SCC
+ *
+ * @brief Decrypt data into a slot in the SCC
+ *
+ * @param info user-space ptr to slot and data information
+ *
+ * @return 0 if pass; non-zero on error
+ */
+static os_error_code sah_handle_scc_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 = scc_decrypt_slot(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;
+}
+
+/*!
+ * Encrypt data in a slot in the SCC
+ *
+ * @brief Encrypt data in a slot in the SCC
+ *
+ * @param info slot data and target location information
+ *
+ * @return 0 if pass; non-zero on error
+ */
+static os_error_code sah_handle_scc_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 = scc_encrypt_slot(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;
+}
+
+/*!
+ * 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;
+
+ 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)
+{
+
+ return FSL_RETURN_OK_S;
+}
+
+/*!
+ * 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 =
+ finished_request->current_dar;
+ 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((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 * uco)
+{
+ os_send_signal(uco->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 someting 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..f4341b5748e0
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_hardware_interface.c
@@ -0,0 +1,862 @@
+/*
+ * 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_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);
+void sah_Dump_Words(const char *prefix, const unsigned *data, dma_addr_t addr,
+ unsigned length);
+
+/* 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);
+#ifdef USE_OLD_PTRS
+ random_desc->desc.ptr1 = (void *)rand_dma;
+ random_desc->desc.original_ptr1 =
+ (void *)random_data_ptr;
+#else
+ random_desc->desc.hw_ptr1 = rand_dma;
+ random_desc->desc.ptr1 = (void *)random_data_ptr;
+#endif
+ random_desc->desc.len2 = 0; /* not used */
+ random_desc->desc.ptr2 = 0; /* not used */
+#ifdef USE_OLD_PTRS
+ random_desc->desc.next = 0; /* chain terminates here */
+ random_desc->desc.original_next = 0; /* chain terminates here */
+#else
+ random_desc->desc.hw_next = 0; /* chain terminates here */
+ random_desc->desc.next = 0; /* chain terminates here */
+#endif
+ 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..4d87bdb61e8c
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_interrupt_handler.c
@@ -0,0 +1,211 @@
+/*
+ * 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.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>
+
+#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..d338e3a74b1a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_memory_mapper.c
@@ -0,0 +1,2041 @@
+/*
+ * 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_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_SCC
+#include <asm/arch/mxc_scc_driver.h>
+#else
+#include <asm/arch/mxc_scc2_driver.h>
+#endif
+
+#if defined(DIAG_DRV_IF) || defined(DIAG_MEM) || defined(DO_DBG)
+#include <diagnostic.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
+
+/* Number of bytes the hardware uses out of sah_Link and sah_*Desc structs */
+#define SAH_HW_LINK_LEN 12
+#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 */
+
+/*!
+*******************************************************************************
+* 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 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(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
+ sprintf(Diag_msg,
+ "User flags: %x; User Reference: %x",
+ head_desc->uco_flags,
+ head_desc->user_ref);
+ LOG_KDIAG(Diag_msg);
+#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
+ sprintf(Diag_msg, "Alloc_Descriptor returned %p\n",
+ curr_desc);
+ LOG_KDIAG(Diag_msg);
+#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(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(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, 3);
+#endif
+ link->vm_info = NULL;
+
+ /* need to retrieve sorted key? */
+ if (link->flags & SAH_STORED_KEY_INFO) {
+ uint32_t max_len = 0; /* max slot length */
+ scc_return_t scc_status;
+
+ /* get length and physical address of stored key */
+ scc_status = scc_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */
+ NULL, /* key length */
+ &max_len);
+ if ((scc_status != SCC_RET_OK) || (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_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("Linc", link, 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, 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, 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)) {
+
+ /* */
+ 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, 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(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(&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(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) {
+ scc_return_t scc_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 */
+ scc_status = scc_get_slot_info(ptr->ownerid, ptr->slot, (uint32_t *) & link->data, /* RED key address */
+ NULL, &max_len);
+
+ if ((scc_status == SCC_RET_OK) && (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 (scc_status == SCC_RET_OK) {
+ 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;
+ }
+ }
+ /* stored-key support */
+ 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(CONFIG_OUTER_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(CONFIG_OUTER_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(CONFIG_OUTER_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(CONFIG_OUTER_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..8afaf17c28ca
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_queue_manager.c
@@ -0,0 +1,1033 @@
+/*
+ * 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.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
+ /* 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..5b5e67bda125
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sah_status_manager.c
@@ -0,0 +1,684 @@
+/*
+ * 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.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;
+
+ /* HW status at time of interrupt */
+ hw_status &= SAH_EXEC_STATE_MASK;
+
+ do {
+ sah_Head_Desc *current_entry;
+ uint32_t dar;
+
+#ifdef DIAG_INT_COUNT
+ if (hw_status == SAH_EXEC_DONE1) {
+ done1_count++;
+ } else if (hw_status == SAH_EXEC_DONE1_BUSY2) {
+ done1busy2_count++;
+ } else if (hw_status == SAH_EXEC_DONE1_DONE2) {
+ done1done2_count++;
+ }
+#endif
+
+ /* if the first entry on sahara has completed... */
+ if ((hw_status & SAH_EXEC_DONE1_BIT) ||
+ (hw_status == 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 hw_status to avoid an infinite loop (possible if
+ * hw_status 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;
+
+ /* SAHARA is reporting an error with descriptor chain 1 */
+ if (hw_status == SAH_EXEC_ERROR1) {
+ /* Gather extra diagnostic information */
+ current_entry->fault_address =
+ sah_HW_Read_Fault_Address();
+ current_entry->current_dar =
+ sah_HW_Read_CDAR();
+ /* Read this last - it clears the error */
+ current_entry->error_status =
+ sah_HW_Read_Error_Status();
+ } else {
+ /* indicate that no errors were found with descriptor
+ * chain 1 */
+ current_entry->error_status = 0;
+
+ /* is there a second, successfully, completed descriptor
+ * chain? (done1/error2 processing is handled later) */
+ if (hw_status == 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 (hw_status == 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 (hw_status == SAH_EXEC_BUSY) {
+ LOG_KDIAG
+ ("Got Sahara interrupt in Busy state");
+ } else {
+ if (hw_status == 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 */
+ hw_status = (hw_status == SAH_EXEC_DONE1_ERROR2) ?
+ SAH_EXEC_ERROR1 : SAH_EXEC_IDLE;
+
+ /* Keep going while further status is available. */
+ } while (hw_status == SAH_EXEC_ERROR1);
+
+ 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->current_dar = 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();
+ entry->current_dar = sah_HW_Read_CDAR();
+ /* Read this register last - it clears the error */
+ entry->error_status =
+ sah_HW_Read_Error_Status();
+ /* 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->current_dar,
+ entry->error_status,
+ entry->fault_address);
+#endif
+ } else if (hw_status == SAH_EXEC_OPSTAT1) {
+ uint32_t op_status = sah_HW_Read_Op_Status();
+ entry->result =
+ sah_convert_op_status(op_status);
+ } else {
+ /* SAHARA entered FAULT state (or something bazaar has
+ * happened) */
+ 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..4bd2b3ac6f8a
--- /dev/null
+++ b/drivers/mxc/security/sahara2/sf_util.c
@@ -0,0 +1,1289 @@
+/*
+ * 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 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);
+
+ 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_UGLY
+ 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_UGLY
+ 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) {
+ (*link)->slot = key_info->handle;
+ (*link)->ownerid = key_info->userid;
+ (*link)->data = 0;
+ flags |= SAH_STORED_KEY_INFO;
+ status = FSL_RETURN_OK_S;
+ } 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 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) {
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+ }
+
+ 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;
+}
+
+
+/**
+ * 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) {
+ status = sah_Create_Key_Link(mu, &link2, key_info);
+ }
+
+ if (status != FSL_RETURN_OK_S) {
+ if (link1 != NULL) {
+ sah_Destroy_Link(mu, link1);
+ }
+ if (link2 != NULL) {
+ sah_Destroy_Link(mu, link2);
+ }
+ } else {
+ link2->flags |= SAH_OUTPUT_LINK;
+
+ status = sah_Append_Desc(mu, desc_chain, header, link1, 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, 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, key_info);
+
+ 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;
+}
+
+
+/**
+ * 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 sah_util.c */
diff --git a/drivers/mxc/security/scc2_driver.c b/drivers/mxc/security/scc2_driver.c
new file mode 100644
index 000000000000..85c2a738aac5
--- /dev/null
+++ b/drivers/mxc/security/scc2_driver.c
@@ -0,0 +1,2854 @@
+/*
+ * 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 scc2_driver.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.
+ */
+
+#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 <asm/arch/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>
+
+#define SAHARA_PART_NO 4
+#define VPU_PART_NO 0
+
+/*!
+ * 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);
+
+uint32_t scm_ram_phys_base = SCM_RAM_BASE_ADDR;
+
+void *scm_ram_base = NULL;
+
+/*!
+ * Starting address of Sahara key partition
+ */
+uint8_t *sahara_partition_base;
+dma_addr_t sahara_partition_phys;
+
+uint8_t *scm_black_part_virt;
+uint32_t scm_black_part_phys;
+
+uint8_t *scm_red_part_virt;
+uint32_t scm_red_part_cmd;
+/*!
+ * Starting address of VPU Partition
+ */
+uint8_t *vpu_partition_base;
+dma_addr_t vpu_partition_phys;
+/*! 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_1,
+ .driver_minor_version = SCC_DRIVER_MINOR_VERSION_97,
+ .scm_version = -1,
+ .smn_version = -1,
+ .block_size_bytes = -1,
+ .partition_size_bytes = -1,
+ .partition_count = -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 SCM address space */
+//static uint32_t scm_highest_memory_address;
+
+#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];
+
+/*! Fixed padding for appending to plaintext to fill out a block */
+static uint8_t scc_block_padding[16] =
+ { SCC_DRIVER_PAD_CHAR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static scc_return_t make_sahara_partition(void);
+uint8_t make_vpu_partition(void);
+/******************************************************************************
+ *
+ * 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;
+ }
+
+#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
+
+ /* See whether there is an SCC available */
+ if (0 && !SCC_ENABLED()) {
+ 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) {
+ 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;
+ }
+
+ scm_ram_base =
+ (void *)ioremap_nocache(scm_ram_phys_base,
+ scc_configuration.partition_count *
+ scc_configuration.
+ partition_size_bytes);
+ if (scm_ram_base == NULL) {
+ 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);
+
+ return_value = make_sahara_partition();
+ if (return_value != SCC_RET_OK) {
+ scc_availability = SCC_STATUS_UNIMPLEMENTED;
+ goto out;
+ }
+
+ /* 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].part_ctl =
+ (((i * SCC_KEY_SLOT_SIZE) /
+ SCC_BLOCK_SIZE_BYTES() << SCM_CCMD_OFFSET_SHIFT)
+ | (SAHARA_PART_NO << SCM_CCMD_PART_SHIFT));
+ scc_key_info[i].status = 0; /* unassigned */
+ }
+
+ 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 */
+ printk("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");
+
+ 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. */
+ 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);
+ }
+
+ if (sahara_partition_base != NULL) {
+
+ }
+ /* Now that interrupts cannot occur, disassociate driver from the interrupt
+ * lines.
+ */
+
+ /* Deregister SCM interrupt handler */
+ if (scm_irq_set) {
+ free_irq(INT_SCC_SCM, NULL);
+ }
+
+ /* Deregister SMN interrupt handler */
+ if (smn_irq_set) {
+#ifdef USE_SMN_INTERRUPT
+ free_irq(INT_SCC_SMN, NULL);
+#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_crypt() */
+/*****************************************************************************/
+scc_return_t
+scc_crypt(unsigned long count_in_bytes, uint8_t * data_in,
+ 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)
+{
+ uint32_t scm_command = scm_red_part_cmd;
+ unsigned long irq_flags; /* for IRQ save/restore */
+ unsigned locked = FALSE;
+ 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("SCC2: scc_crypt() detected bad argument\n");
+ goto out;
+ }
+ /* Lock access to crypto memory of the SCC */
+ spin_lock_irqsave(&scc_crypto_lock, irq_flags);
+ locked = TRUE;
+ /* Special needs for CBC Mode */
+ if (crypto_mode == SCC_CBC_MODE) {
+ scm_command |= SCM_CCMD_CBC; /* change default of ECB */
+ /* Put in Initial Context. Vector registers are contiguous */
+ copy_to_scc(init_vector,
+ (uint32_t) (scc_base + SCM_AES_CBC_IV0_REG),
+ SCC_BLOCK_SIZE_BYTES(), NULL);
+ }
+
+ /* Fill the BLACK_START register */
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, scm_black_part_phys);
+
+ 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;
+ goto out;
+ }
+ } 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;
+ goto out;
+ }
+ }
+ return_code =
+ scc_encrypt(count_in_bytes, data_in, scm_command, 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
+ ("SCC2: insufficient decrypt space %ld/%d.\n",
+ *count_out_bytes, possible_size);
+ return_code = SCC_RET_INSUFFICIENT_SPACE;
+ goto out;
+ }
+ }
+
+ return_code =
+ scc_decrypt(count_in_bytes, data_in, scm_command, data_out,
+ check_mode == SCC_VERIFY_MODE_CCITT_CRC,
+ count_out_bytes);
+ } /* SCC_DECRYPT */
+
+ out:
+ /* unlock the SCC */
+ if (locked) {
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+ }
+
+ 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_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;
+ 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_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.
+ *
+ * @param irq Channel number for the IRQ. (@c SCC_INT_SMN or @c SCC_INT_SCM).
+ * @param dev_id Pointer to the identification of the device. Ignored.
+ */
+static irqreturn_t scc_irq(int irq, void *dev_id)
+{
+ 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 ((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 (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 */
+ 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 */
+
+ pr_debug("SCC2: copying %ld bytes to 0x%0x.\n", count_bytes, to);
+
+ 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 = *(uint32_t *) 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 */
+
+#ifdef __BIG_ENDIAN
+ 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) {
+ *(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) {
+ *(uint32_t *) SCC_WORD_PTR(to) = scm_word; /* save */
+ }
+
+ /* Copy CRC back */
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ return SCC_RET_OK;
+}
+
+/*****************************************************************************/
+/* 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 */
+
+ pr_debug("SCC2: copying %ld bytes from 0x%x.\n", count_bytes, from);
+
+ if (crc) {
+ current_crc = *crc;
+ }
+
+ /* Read word which is sitting in SCM memory. Ignore byte offset */
+ scm_word = *(uint32_t *) SCC_WORD_PTR(running_from);
+ pr_debug("%08x ", scm_word);
+ /* If necessary, move the 'first' byte into place */
+ if (SCC_BYTE_OFFSET(running_from) != 0) {
+#ifdef __BIG_ENDIAN
+ 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 */
+
+#ifdef __BIG_ENDIAN
+ 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 = *(uint32_t *) running_from;
+ pr_debug("%08x ", scm_word);
+ }
+ }
+
+ pr_debug("\n");
+ if (crc) {
+ *crc = current_crc;
+ }
+
+ return SCC_RET_OK;
+}
+
+/*****************************************************************************/
+/* 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("SCC2: 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_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_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();
+ printk(KERN_ERR "SCC2: SCC went into FAILED mode\n");
+ } else {
+ /* START, ZEROIZE RAM, HEALTH CHECK, or unknown */
+ scc_availability = SCC_STATUS_UNIMPLEMENTED; /* unuseable */
+ 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 = request_irq(INT_SCC_SMN, scc_irq, 0,
+ SCC_DRIVER_NAME, NULL);
+ if (smn_error_code != 0) {
+ printk
+ ("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 = request_irq(INT_SCC_SCM, scc_irq, 0,
+ SCC_DRIVER_NAME, NULL);
+ if (scm_error_code != 0) {
+#ifndef MXC
+ printk
+ ("SCC2 Driver: Error installing SCM Interrupt Handler: %d\n",
+ scm_error_code);
+#else
+ printk
+ ("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_ERROR_STATUS 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;
+
+ /* 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;
+}
+
+/*****************************************************************************/
+/* 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, 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 = (uint32_t) scm_red_part_virt;
+ uint32_t scm_bytes_remaining = scm_memory_size_bytes; /* free RED RAM */
+ uint8_t padding_buffer[PADDING_BUFFER_MAX_BYTES]; /* CRC+padding holder */
+ unsigned padding_byte_count = 0; /* Reset if padding required */
+ uint32_t scm_error_status = 0; /* No known SCM error initially */
+
+ scm_control |= SCM_CCMD_ENC;
+ /* 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 - (uint32_t) scm_red_part_virt;
+
+ if (output_bytes_copied + bytes_to_process > *count_out_bytes) {
+ return_code = SCC_RET_INSUFFICIENT_SPACE;
+ scm_error_status = -1; /* error signal */
+ pr_debug
+ ("SCC2: too many ciphertext bytes for space available\n");
+ break;
+ }
+ pr_debug("SCC2: Starting encryption. %x for %d bytes (%p)\n",
+ scm_control, bytes_to_process,
+ (void *)SCC_READ_REGISTER(SCM_C_BLACK_ST_REG));
+ scm_error_status = scc_do_crypto(bytes_to_process, scm_control);
+ if (scm_error_status != 0) {
+ break;
+ }
+
+ /* Copy out ciphertext */
+ copy_from_scc((uint32_t) scm_black_part_virt, 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 = (uint32_t) scm_red_part_virt;
+ scm_bytes_remaining = scm_memory_size_bytes;
+ } /* input_bytes_left > 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, 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 = (uint32_t) scm_black_part_virt;
+ 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 */
+
+ scm_control |= SCM_CCMD_DEC;
+ 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 > (scm_memory_size_bytes)) {
+ bytes_to_copy = scm_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("SCC2: 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((uint32_t) scm_red_part_virt, data_out,
+ bytes_to_copy, &crc);
+ bytes_copied += bytes_to_copy;
+ data_out += bytes_to_copy;
+ scm_location = (uint32_t) scm_black_part_virt;
+
+ /* 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("SCC2: 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((uint32_t) scm_red_part_virt,
+ 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
+ ("SCC2: 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 */
+ 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("SCC2: Allocating %d-byte slot\n", value_size_bytes);
+
+ 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("SCC2: Allocated slot %d\n", i);
+ }
+ }
+
+ 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("SCC2: Verify on slot %d succeeded\n", slot);
+ } else {
+ if (slot >= SCC_KEY_SLOTS) {
+ pr_debug("SCC2: Verify on bad slot (%d) failed\n",
+ slot);
+ } else if (scc_key_info[slot].status) {
+ pr_debug("SCC2: Verify on slot %d failed (%Lx) \n",
+ slot, owner_id);
+ } else {
+ pr_debug
+ ("SC2C: Verify on slot %d failed: not allocated\n",
+ slot);
+ }
+ }
+
+ out:
+ return status;
+}
+
+/*****************************************************************************/
+/* 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;
+ uint8_t *slot_loc = NULL;
+ 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) {
+ goto out;
+ }
+
+ scc_key_info[slot].owner_id = 0;
+ scc_key_info[slot].status = 0; /* unassign */
+ slot_loc = sahara_partition_base + scc_key_info[slot].offset;
+
+ for (i = 0; i < SCC_KEY_SLOT_SIZE; i++) {
+ slot_loc[i] = 0;
+ }
+ pr_debug("SCC2: Deallocated slot %d\n", slot);
+
+ out:
+ 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, 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
+ ("SCC2: scc_load_slot() rejecting key of %d bytes.\n",
+ key_length);
+ status = SCC_RET_INSUFFICIENT_SPACE;
+ } else {
+ if (copy_to_scc(key_data,
+ (uint32_t) sahara_partition_base +
+ scc_key_info[slot].offset, key_length,
+ NULL)) {
+ pr_debug("SCC2: RED copy_to_scc() failed for"
+ " scc_load_slot()\n");
+ } else {
+ status = SCC_RET_OK;
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc_crypto_lock, irq_flags);
+
+ return status;
+} /* scc_load_slot */
+
+/*****************************************************************************/
+/* fn scc_encrypt_slot() */
+/*****************************************************************************/
+/*!
+ * 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.
+ */
+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 scm_command = scc_key_info[slot].part_ctl;
+
+ /* 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_C_BLACK_ST_REG, scm_black_part_phys);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG,
+ *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG,
+ *(((uint32_t *) & owner_id) + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, 0);
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, 0);
+
+ /* Set modes and kick off the encryption */
+ crypto_status =
+ scc_do_crypto(length, scm_command | SCM_CCMD_AES_ENC_CBC);
+
+ 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
+ ((uint32_t) scm_black_part_virt, black_data, length,
+ NULL)) {
+ status = SCC_RET_OK;
+ pr_debug
+ ("SCC2: Encrypted slot %d for %d bytes\n",
+ slot, length);
+ }
+ }
+ }
+
+ 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 scm_command = scc_key_info[slot].part_ctl;
+
+ /* 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,
+ (uint32_t) scm_black_part_virt, length, NULL);
+
+ SCC_WRITE_REGISTER(SCM_C_BLACK_ST_REG, scm_black_part_phys);
+
+ /* Use OwnerID as CBC IV to tie Owner to data */
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV0_REG,
+ *(uint32_t *) & owner_id);
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV1_REG,
+ *(((uint32_t *) & owner_id) + 1));
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV2_REG, 0);
+ SCC_WRITE_REGISTER(SCM_AES_CBC_IV3_REG, 0);
+
+ /* Set modes and kick off the decryption */
+ crypto_status = scc_do_crypto(length,
+ scm_command |
+ SCM_CCMD_AES_DEC_CBC);
+
+ 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 slot %d for %d bytes\n", slot,
+ length);
+ }
+ }
+
+ 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 = sahara_partition_phys + 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;
+}
+
+/*!
+ * For now, this function will create a shared Sahara and SCC2 partition. It
+ * will be used as a key store for Sahara and, to mimic SCCv1 behavior, as the
+ * temporary black and red memories for ephemeral cipher operations.
+ *
+ * This means that it will not be secure against kernel access, and in fact
+ * must be available for host read/write.
+ *
+ * *========================================*
+ * * Key Slot 0 *
+ * * Key Slot 1 *
+ * * Key Slot ... *
+ * * Key Slot n *
+ * * -------------------------------------- *
+ * * *
+ * * 'BLACK RAM' *
+ * * *
+ * * -------------------------------------- *
+ * * *
+ * * 'RED RAM' *
+ * * *
+ * *========================================*
+ *
+ * or -- the BLACK RAM gets put in a separate partition...
+ */
+static
+scc_return_t make_sahara_partition()
+{
+ unsigned part_no = SAHARA_PART_NO; /* better be free!! */
+ int retval = -EIO;
+ uint32_t *part_base =
+ scm_ram_base + (part_no * scc_configuration.partition_size_bytes);
+ uint32_t reg_value;
+
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ /* Store SMID to grab a partition */
+ SCC_WRITE_REGISTER(SCM_SMID0_REG + 8 * part_no, 0x00000000);
+ mdelay(2);
+
+ /* Now make sure it is ours... ? */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ if (((reg_value >> (2 * part_no)) & 0x3) != 3) {
+ printk(KERN_ERR "Could not acquire partition %u\n", part_no);
+ goto out;
+ }
+ sahara_partition_base = (uint8_t *) part_base;
+
+ pr_debug("SCC2 Writing UMID at %p\n", part_base);
+
+ /* Write in the UMID */
+ part_base[4] = 0x42;
+ part_base[5] = 0x43;
+ part_base[6] = 0x19;
+ part_base[7] = 0x59;
+
+ mdelay(2);
+
+ /* Give both host and Sahara access for read/write */
+ part_base[0] =
+ SCM_PERM_HD_WRITE | SCM_PERM_HD_READ | SCM_PERM_TH_READ |
+ SCM_PERM_TH_WRITE;
+ mdelay(2);
+
+ reg_value = SCC_READ_REGISTER(SCM_PART_ENGAGED_REG);
+
+ if (((reg_value >> part_no) & 1) != 1) {
+ printk(KERN_ERR "SCC2 Could not engage partition %u\n",
+ part_no);
+ retval = SCC_RET_FAIL;
+ goto out;
+ }
+// (void)SCC_READ_REGISTER(SCM_ACC4_REG);
+
+ sahara_partition_phys =
+ (uint32_t) scm_ram_phys_base +
+ (part_no * scc_configuration.partition_size_bytes);
+
+ scm_black_part_virt =
+ sahara_partition_base + (SCC_KEY_SLOTS * SCC_KEY_SLOT_SIZE);
+ scm_black_part_phys =
+ sahara_partition_phys + (scm_black_part_virt -
+ sahara_partition_base);
+
+ scm_memory_size_bytes = 256;
+ scm_red_part_virt = scm_black_part_virt + scm_memory_size_bytes;
+ if ((uint32_t) (scm_red_part_virt - sahara_partition_phys) < 256) {
+ printk(KERN_ERR
+ "SCC2: not enough space in Sahara partition: too many / too large keys\n");
+ retval = SCC_RET_INSUFFICIENT_SPACE;
+ goto out;
+ }
+
+ scm_red_part_cmd = (((part_no << SCM_CCMD_PART_SHIFT)
+ | ((scm_red_part_virt - sahara_partition_base) /
+ SCC_BLOCK_SIZE_BYTES())
+ << SCM_CCMD_OFFSET_SHIFT)
+ | SCM_CCMD_AES);
+
+ pr_debug
+ ("SCC2: Sahara partition %08x/%p; Black RAM: %08x/%p; Red RAM: %p\n",
+ sahara_partition_phys, sahara_partition_base, scm_black_part_phys,
+ scm_black_part_virt, scm_red_part_virt);
+
+ retval = SCC_RET_OK;
+
+ out:
+ return retval;
+} /* make_sahara_partition() */
+
+uint8_t make_vpu_partition()
+{
+ unsigned part_no = VPU_PART_NO; /* better be free!! */
+ uint32_t *part_base =
+ scm_ram_base + (part_no * scc_configuration.partition_size_bytes);
+ uint32_t reg_value;
+
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+
+ /* Store SMID to grab a partition */
+ for (; part_no < 4; part_no++)
+ SCC_WRITE_REGISTER(SCM_SMID0_REG + 8 * part_no, 0x00000000);
+
+ mdelay(2);
+
+ /* Now make sure it is ours... ? */
+ reg_value = SCC_READ_REGISTER(SCM_PART_OWNERS_REG);
+ for (; part_no < 4; part_no++) {
+ if (((reg_value >> (2 * part_no)) & 0x3) != 3) {
+ printk(KERN_ERR "Could not acquire partition %u\n",
+ part_no);
+ goto out;
+ }
+ }
+ vpu_partition_base = (uint8_t *) part_base;
+
+ mdelay(2);
+ for (; part_no < 4; part_no++) {
+ part_base =
+ scm_ram_base +
+ (part_no * scc_configuration.partition_size_bytes);
+ part_base[0] =
+ SCM_PERM_HD_WRITE | SCM_PERM_HD_READ | SCM_PERM_TH_READ |
+ SCM_PERM_TH_WRITE;
+ }
+ mdelay(2);
+
+ reg_value = SCC_READ_REGISTER(SCM_PART_ENGAGED_REG);
+
+ if (((reg_value >> part_no) & 1) != 1) {
+ printk(KERN_ERR "SCC2 Could not engage partition %u\n",
+ part_no);
+ goto out;
+ }
+ part_no = VPU_PART_NO;
+ vpu_partition_phys =
+ (uint32_t) scm_ram_phys_base +
+ (part_no * scc_configuration.partition_size_bytes);
+
+ return vpu_partition_phys;
+
+ out:
+ return 0;
+} /* make_vpu_partition */
+
+/*****************************************************************************/
+/* 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 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;
+ 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.
+ */
+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 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_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..90a32eb57397
--- /dev/null
+++ b/drivers/mxc/security/scc2_internals.h
@@ -0,0 +1,512 @@
+/*
+ * 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 SCC_INTERNALS_H
+#define SCC_INTERNALS_H
+
+/** @file 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. 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 <asm/io.h> /* ioremap() */
+#include <linux/interrupt.h> /* IRQ / interrupt definitions */
+
+
+#include <asm/arch/mxc_scc2_driver.h>
+
+#if defined(MXC)
+
+#include <asm/arch/iim.h>
+#include <asm/arch/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
+
+
+/**
+ * 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 */
+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 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_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);
+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("Proprietary");
+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..e91b7bc3fce5
--- /dev/null
+++ b/drivers/mxc/ssi/registers.h
@@ -0,0 +1,208 @@
+/*
+ * 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/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 <asm/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..284090a3ef1f
--- /dev/null
+++ b/drivers/mxc/ssi/ssi.c
@@ -0,0 +1,1238 @@
+/*
+ * 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.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/platform_device.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <asm/arch/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..d05c54311b36
--- /dev/null
+++ b/drivers/mxc/vpu/Kconfig
@@ -0,0 +1,21 @@
+#
+# 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)
+ default y
+ ---help---
+ The VPU codec device provides codec function for H.264/MPEG4/H.263
+
+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..cd062d28aef5
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vl2cc.c
@@ -0,0 +1,123 @@
+/*
+ * 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 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 <asm/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..be9e443f83a8
--- /dev/null
+++ b/drivers/mxc/vpu/mxc_vpu.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright 2006-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_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 <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/dma-mapping.h>
+#include <asm/hardware.h>
+
+#include <asm/arch/mxc_vpu.h>
+
+#define BIT_INT_CLEAR 0x00C
+#define BIT_INT_STATUS 0x010
+#define BIT_INT_ENABLE 0x170
+
+typedef struct vpu_t {
+ struct fasync_struct *async_queue;
+} vpu_t;
+
+/* To track the allocated memory buffer */
+typedef struct memalloc_record {
+ struct list_head list;
+ vpu_mem_desc mem;
+} memalloc_record;
+
+static DEFINE_SPINLOCK(vpu_lock);
+static LIST_HEAD(head);
+
+static int vpu_major = 0;
+static struct class *vpu_class;
+static struct vpu_t vpu_data;
+static u8 open_count = 0;
+static struct clk *vpu_clk;
+
+/* implement the blocking ioctl */
+static int codec_done = 0;
+static wait_queue_head_t vpu_queue;
+
+/*!
+ * Private function to free buffers
+ * @return status 0 success.
+ */
+static int vpu_free_buffers(void)
+{
+ struct memalloc_record *rec, *n;
+ vpu_mem_desc mem;
+
+ spin_lock(&vpu_lock);
+ list_for_each_entry_safe(rec, n, &head, list) {
+ mem = rec->mem;
+ if (mem.cpu_addr != 0) {
+ dma_free_coherent(0, PAGE_ALIGN(mem.size),
+ (void *)mem.cpu_addr, mem.phy_addr);
+ pr_debug("[FREE] freed paddr=0x%08X\n", mem.phy_addr);
+
+ /* delete from list */
+ list_del(&rec->list);
+ kfree(rec);
+ }
+ }
+ spin_unlock(&vpu_lock);
+
+ return 0;
+}
+
+/*!
+ * @brief vpu interrupt handler
+ */
+static irqreturn_t vpu_irq_handler(int irq, void *dev_id)
+{
+ struct vpu_t *dev;
+ dev = (struct vpu_t *)dev_id;
+ __raw_readl(IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_STATUS));
+ __raw_writel(0x1, IO_ADDRESS(VPU_BASE_ADDR + BIT_INT_CLEAR));
+ if (dev->async_queue)
+ kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
+
+ codec_done = 1;
+ wake_up_interruptible(&vpu_queue);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * @brief vpu hardware enable function
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_hardware_enable(void)
+{
+ if (cpu_is_mx32()) {
+ vl2cc_enable();
+ }
+ clk_enable(vpu_clk);
+ return 0;
+}
+
+/*!
+ * @brief vpu hardware disable function
+ *
+ * @return 0 on success or negative error code on error
+ */
+static int vpu_hardware_disable(void)
+{
+ if (cpu_is_mx32()) {
+ vl2cc_disable();
+ }
+ clk_disable(vpu_clk);
+ return 0;
+
+}
+
+/*!
+ * @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)
+{
+ if (open_count++ == 0) {
+ filp->private_data = (void *)(&vpu_data);
+ vpu_hardware_enable();
+ } else {
+ printk(KERN_ERR "VPU has already been opened.\n");
+ return -EACCES;
+ }
+
+ 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), (vpu_mem_desc *) arg,
+ sizeof(vpu_mem_desc));
+ if (ret) {
+ kfree(rec);
+ return -EFAULT;
+ }
+
+ pr_debug("[ALLOC] mem alloc size = 0x%x\n",
+ rec->mem.size);
+ rec->mem.cpu_addr = (unsigned long)
+ dma_alloc_coherent(NULL,
+ PAGE_ALIGN(rec->mem.size),
+ (dma_addr_t
+ *) (&(rec->mem.phy_addr)),
+ GFP_DMA | GFP_KERNEL);
+ pr_debug("[ALLOC] mem alloc cpu_addr = 0x%x\n",
+ rec->mem.cpu_addr);
+ if ((void *)(rec->mem.cpu_addr) == NULL) {
+ kfree(rec);
+ printk(KERN_ERR
+ "Physical memory allocation error!\n");
+ ret = -1;
+ break;
+ }
+ ret = copy_to_user((void __user *)arg, &(rec->mem),
+ sizeof(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;
+ vpu_mem_desc vpu_mem;
+
+ ret = copy_from_user(&vpu_mem, (vpu_mem_desc *) arg,
+ sizeof(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) {
+ dma_free_coherent(NULL,
+ PAGE_ALIGN(vpu_mem.size),
+ (void *)vpu_mem.cpu_addr,
+ (dma_addr_t) vpu_mem.
+ phy_addr);
+ }
+
+ 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;
+ }
+
+ codec_done = 0;
+ break;
+ }
+ case VPU_IOC_VL2CC_FLUSH:
+ if (cpu_is_mx32()) {
+ vl2cc_flush();
+ }
+ 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)
+{
+ if (--open_count == 0) {
+ vpu_free_buffers();
+ vpu_hardware_disable();
+ }
+
+ 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_t *dev = (struct vpu_t *)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 class_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 -ENOENT;
+ }
+
+ err = vl2cc_init(res->start);
+ if (err != 0)
+ return err;
+ }
+
+ 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 = 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(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:
+ 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;
+}
+
+/*! Driver definition
+ *
+ */
+static struct platform_driver mxcvpu_driver = {
+ .driver = {
+ .name = "mxc_vpu",
+ },
+ .probe = vpu_dev_probe,
+};
+
+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(INT_VPU, (void *)(&vpu_data));
+ if (vpu_major > 0) {
+ class_device_destroy(vpu_class, MKDEV(vpu_major, 0));
+ class_destroy(vpu_class);
+ if (unregister_chrdev(vpu_major, "mxc_vpu") < 0) {
+ printk(KERN_ERR
+ "Failed to unregister vpu from devfs\n");
+ return;
+ }
+ vpu_major = 0;
+ }
+
+ if (cpu_is_mx32()) {
+ vl2cc_cleanup();
+ }
+
+ clk_put(vpu_clk);
+
+ platform_driver_unregister(&mxcvpu_driver);
+ return;
+}
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX27");
+MODULE_LICENSE("GPL");
+
+module_init(vpu_init);
+module_exit(vpu_exit);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 9af05a2f4af3..323f5e7c0c39 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1807,11 +1807,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_MX33
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)"
diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
index 571750975137..1270ac305edc 100644
--- a/drivers/net/cs89x0.c
+++ b/drivers/net/cs89x0.c
@@ -194,6 +194,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[] __initdata = {CIRRUS_DEFAULT_BASE, 0};
static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 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[] __initdata =
{ 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
@@ -802,7 +813,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
} else {
i = lp->isa_config & INT_NO_MASK;
if (lp->chip_type == CS8900) {
-#if defined(CONFIG_MACH_IXDP2351) || defined(CONFIG_ARCH_IXDP2X01) || defined(CONFIG_ARCH_PNX010X)
+#if defined(CONFIG_MACH_IXDP2351) || defined(CONFIG_ARCH_IXDP2X01) || defined(CONFIG_ARCH_PNX010X) || defined(CONFIG_ARCH_MXC)
i = cs8900_irq_map[0];
#else
/* Translate the IRQ using the IRQ mapping table. */
@@ -1029,6 +1040,7 @@ skip_this_frame:
void __init reset_chip(struct net_device *dev)
{
+#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;
@@ -1057,6 +1069,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_ARCH_MXC */
}
@@ -1304,7 +1317,7 @@ net_open(struct net_device *dev)
else
#endif
{
-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X)
+#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_ARCH_MXC)
if (((1 << dev->irq) & lp->irq_map) == 0) {
printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index 0fbf1bbbaee9..99b3efd77738 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -24,6 +24,9 @@
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
* Copyright (c) 2004-2006 Macq Electronique SA.
*/
+/*
+ * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
#include <linux/module.h>
#include <linux/kernel.h>
@@ -42,6 +45,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
+#include <linux/clk.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -55,12 +59,21 @@
#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 <asm/arch/hardware.h>
+#include <asm/arch/iim.h>
+#include "fec.h"
+#define FEC_ALIGNMENT (0x0F) /*FEC needs 128bits(32bytes) alignment*/
#else
#include <asm/8xx_immap.h>
#include <asm/mpc8xx.h>
#include "commproc.h"
+#define FEC_ALIGNMENT (0x03) /*FEC needs 4bytes 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
#else
@@ -82,6 +95,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
@@ -159,6 +174,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
@@ -172,7 +193,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
@@ -195,11 +216,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 */
@@ -213,6 +236,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;
@@ -224,6 +248,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);
@@ -238,6 +264,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 = 192;
/* MII processing. We keep this as simple as possible. Requests are
* placed on the list (if there is room). When the request is finished
@@ -346,10 +383,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]);
}
@@ -363,8 +400,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);
spin_lock_irq(&fep->lock);
@@ -379,7 +416,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.
*/
@@ -465,7 +502,8 @@ 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)) {
+
handled = 1;
fec_enet_rx(dev);
}
@@ -474,7 +512,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)) {
handled = 1;
fec_enet_tx(dev);
}
@@ -576,6 +614,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();
@@ -590,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.
@@ -634,20 +673,37 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
pkt_len = bdp->cbd_datlen;
dev->stats.rx_bytes += pkt_len;
data = (__u8*)__va(bdp->cbd_bufaddr);
+ fec_dcache_inv_range(data, data+pkt_len -4);
/* This does 16 byte alignment, exactly what we need.
* The packet length includes FCS, but we don't want to
* 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 */
+ 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);
+ pkt_len-4, 0);
+ } else {
+ struct sk_buff * pskb = fep->rx_skbuff[rx_index];
+
+ 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);
}
@@ -674,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;
@@ -1234,6 +1290,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)
@@ -1339,10 +1410,35 @@ static void __inline__ fec_localhw_setup(void)
}
/*
- * Do not need to make region uncached on 5272.
+ * 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 void __inline__ fec_uncache(unsigned long addr)
+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 ;
}
/* ------------------------------------------------------------------------- */
@@ -1350,6 +1446,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.
*/
@@ -1509,17 +1621,55 @@ static void __inline__ fec_phy_ack_intr(void)
static void __inline__ fec_localhw_setup(void)
{
}
+/*
+ * 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
@@ -1641,13 +1791,59 @@ static void __inline__ fec_localhw_setup(void)
{
}
-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)
{
+ 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
*/
@@ -1791,16 +1987,213 @@ static void __inline__ fec_localhw_setup(void)
}
/*
- * 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(INT_FEC, fec_enet_interrupt, 0, "fec", dev) != 0)
+ panic("FEC: Could not allocate FEC IRQ(%d)!\n", INT_FEC);
+ /* TODO: disable now due to CPLD issue */
+ if (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 (cpu_is_mx27_rev(CHIP_REV_2_0) > 0) {
+ fec_mac_base = FEC_IIM_BASE + MXC_IIMMAC;
+ }
+
+ /*
+ * 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;
+}
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+ enable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+ disable_irq(expio_intr_fec);
+}
+
+static void __inline__ fec_localhw_setup(void)
+{
+}
+
+/*
+ * invalidate dcache related with the virtual memory range(start, end)
+ */
+static void __inline__ fec_dcache_inv_range(void * start, void * end)
+{
+ dma_sync_single(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(NULL, (unsigned long)__pa(start), (unsigned long) (end-start), DMA_BIDIRECTIONAL);
+ 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.
@@ -1907,12 +2300,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
@@ -1959,6 +2380,7 @@ static void mii_display_config(struct work_struct *work)
{
struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
struct net_device *dev = fep->netdev;
+ struct net_device *dev = fep->net;
uint status = fep->phy_status;
/*
@@ -1996,6 +2418,7 @@ static void mii_relink(struct work_struct *work)
{
struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
struct net_device *dev = fep->netdev;
+ struct net_device *dev = fep->net;
int duplex;
/*
@@ -2142,10 +2565,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;
}
@@ -2190,7 +2617,6 @@ fec_enet_open(struct net_device *dev)
fec_restart(dev, 1);
}
- netif_start_queue(dev);
fep->opened = 1;
return 0; /* Success */
}
@@ -2203,9 +2629,9 @@ 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);
+ }
return 0;
}
@@ -2316,6 +2742,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;
@@ -2324,6 +2751,10 @@ int __init fec_enet_init(struct net_device *dev)
if (index >= FEC_MAX_PORTS)
return -ENXIO;
+ fep->net = dev;
+
+ spin_lock_init(&(fep->lock));
+
/* Allocate memory for buffer descriptors.
*/
mem_addr = __get_free_page(GFP_KERNEL);
@@ -2332,6 +2763,7 @@ int __init fec_enet_init(struct net_device *dev)
return -ENOMEM;
}
+ fep->cbd_mem_base = (void *)mem_addr;
/* Create an Ethernet device instance.
*/
fecp = (volatile fec_t *) fec_hw[index];
@@ -2353,10 +2785,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.
*/
@@ -2371,25 +2807,24 @@ 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;
+ 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--;
@@ -2422,19 +2857,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_hash_table_high = 0;
fecp->fec_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;
dev->base_addr = (unsigned long)fecp;
@@ -2453,11 +2892,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_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
-
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
@@ -2489,9 +2923,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.
@@ -2511,8 +2951,8 @@ fec_restart(struct net_device *dev, int duplex)
/* 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;
@@ -2579,12 +3019,9 @@ 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;
+ fecp->fec_r_des_active = 0x01000000;
- /* Enable interrupts we wish to service.
- */
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
+ netif_start_queue(dev);
}
static void
@@ -2593,6 +3030,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;
@@ -2628,6 +3067,7 @@ static int __init fec_enet_module_init(void)
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));
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 1d421606984f..9a35c5b7bac8 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
@@ -103,12 +103,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 65806956728a..2b635857b354 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -482,5 +482,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 fefbb5909081..21db102d16bd 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -20,6 +20,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
# Old dongle drivers for old SIR drivers
obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o
obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o
diff --git a/drivers/net/irda/mxc_ir.c b/drivers/net/irda/mxc_ir.c
new file mode 100644
index 000000000000..d7d5d07eb38e
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.c
@@ -0,0 +1,1777 @@
+/*
+ * Copyright 2004-2007 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 <asm/hardware.h>
+#include <asm/arch/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..e8d77e86a548
--- /dev/null
+++ b/drivers/net/irda/mxc_ir.h
@@ -0,0 +1,133 @@
+/*
+ * 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_FIRI_REG_H__
+#define __MXC_FIRI_REG_H__
+
+#include <asm/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/otg/Kconfig b/drivers/otg/Kconfig
new file mode 100644
index 000000000000..be5fcbf1bfc2
--- /dev/null
+++ b/drivers/otg/Kconfig
@@ -0,0 +1,52 @@
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/.Makefiles/Kconfig-freescale|20070525074749|60734
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+menu "On-The-Go and USB Peripheral Support"
+
+ config OTG
+ tristate "Support for On-The-Go and USB Peripheral Support"
+ ---help---
+ Configure all or part of the Belcarra OTG Stack
+
+
+ comment ""
+
+ menu "On-The-Go Support Platform Selection"
+ depends on OTG
+
+ source "drivers/otg/hardware/Kconfig-zasevb"
+ source "drivers/otg/hardware/Kconfig-zasevb-arc"
+ source "drivers/otg/hardware/Kconfig-imx31ads"
+ source "drivers/otg/hardware/Kconfig-isp1301"
+
+ endmenu
+
+ source "drivers/otg/Kconfig-otg"
+
+ if ( OTG_ZASEVB != 'n')
+
+ config OTG_NEW_TX_CACHE
+ bool 'Use new macro to synchronize TX cache'
+ default n
+ ---help---
+ Newer kernels need this set. Turn this off if you get TX_CACHE build errors
+
+ endif
+
+
+
+ menu "Targeted Peripherals List (USB Peripheral Function Drivers)"
+ depends on OTG_PLATFORM_OTG || OTG_PLATFORM_USBD
+ #---help---
+ #A list of USB peripheral types that this device
+ #can emulate when it is acting as a peripheral.
+ source "drivers/otg/functions/generic/Kconfig"
+ source "drivers/otg/functions/acm/Kconfig"
+ source "drivers/otg/functions/mouse/Kconfig"
+ source "drivers/otg/functions/msc/Kconfig"
+ source "drivers/otg/functions/network/Kconfig"
+# source "drivers/otg/functions/belcarra/Kconfig"
+# source "drivers/otg/functions/motorola/Kconfig"
+# source "drivers/otg/functions/isotest/Kconfig"
+ endmenu
+
+endmenu
diff --git a/drivers/otg/Kconfig-otg b/drivers/otg/Kconfig-otg
new file mode 100644
index 000000000000..b8ebebdabe71
--- /dev/null
+++ b/drivers/otg/Kconfig-otg
@@ -0,0 +1,243 @@
+#
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/Kconfig-otg|20070531215019|62203
+# Copyright (c) 2005 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+#
+# The platform configuration must define the following capability flags to
+# allow this common selection to work.
+
+# This implements selection of the appropriate role that the software should
+# implement:
+#
+
+# Device and Platform Capabilities
+# Supports Acting as a:
+# OTG_PLATFORM_USBD standard USB peripheral
+# OTG_PLATFORM_USB standard USB host
+# OTG_PLATFORM_OTG OTG Device
+#
+# OTG_HIGH_SPEED_CAPABLE High Speed Capable Device.
+# OTG_REMOTE_WAKEUP_CAPABLE Device can do Remote Wakeup.
+#
+# OTG_BUS_POWERED_CAPABLE Platform can be bus powered.
+#
+
+# Role Configuration
+# Implements:
+# OTG_USB_PERIPHERAL USB peripheral
+# OTG_USB_HOST USB host
+# OTG_USB_PERIPHERAL_OR_HOST USB peripheral or USB host
+# OTG_BDEVICE_WITH_SRP OTG SRP capable B-Device
+# OTG_DEVICE OTG Device
+#
+#
+
+ comment ""
+
+ menu "On-The-Go Options"
+
+ depends on OTG_PLATFORM_OTG|| OTG_PLATFORM_USBD
+
+
+ # USBOTG Role Configuration
+ choice
+ prompt "On-The-Go or USB Device Role Configuration"
+
+ config OTG_USB_PERIPHERAL
+ bool "USB Peripheral (only)"
+ depends on OTG_PLATFORM_USBD
+ ---help---
+ Implement a standard USB Peripheral only.
+
+ config OTG_USB_WIRED_DEVICE
+ bool "USB Wired Device (only)"
+ depends on OTG_PLATFORM_WUSBD
+ ---help---
+ Implement a Wired USB Device only.
+
+ #config OTG_USB_HOST
+ # bool "USB Host (only)"
+ # depends on OTG_PLATFORM_USB
+ # ---help---
+ # Implement a standard USB Host only.
+
+ #config OTG_USB_WIRED_HOST
+ # bool "USB Host (only)"
+ # depends on OTG_PLATFORM_WUSB
+ # ---help---
+ # Implement a Wired USB Host only.
+
+ config OTG_USB_PERIPHERAL_OR_HOST
+ bool "USB Peripheral or Host"
+ depends on OTG_PLATFORM_USB && OTG_PLATFORM_USBD && USB
+ ---help---
+ Implement a standard USB Peripheral and Host.
+ Support for this is platform-dependent.
+
+ config OTG_USB_WIRED_DEVICE_OR_HOST
+ bool "USB Wired Device or Host"
+ depends on OTG_PLATFORM_WUSB && OTG_PLATFORM_WUSBD && USB
+ ---help---
+ Implement a Wired USB Peripheral and Host.
+ Support for this is platform-dependent.
+
+ #config OTG_BDEVICE_WITH_SRP
+ # bool "SRP Capable B-Device (Only)"
+ # depends on OTG_PLATFORM_OTG
+ # ---help---
+ # Implement an On-The-Go Peripheral-Only SRP-capable device. This
+ # is similar to a Traditional USB Peripheral but enables
+ # On-The-Go features such as Service Request Protocol (SRP).
+
+ config OTG_DEVICE
+ bool "OTG Device - can act as A or B Device"
+ depends on OTG_PLATFORM_OTG && USB
+ ---help---
+ Implement full On-The-Go Device support for a platform that
+ supports implementation of A and B Devices that implement
+ Service Request Protocol (SRP) and Host Negotiation Protocol
+ (HNP).
+
+ endchoice
+
+ # High Speed
+ if ( OTG_HIGH_SPEED_CAPABLE = 'y')
+ # we may want to make this conditional
+ config OTG_HIGH_SPEED
+ bool 'Enable High Speed for this device'
+ default OTG_HIGH_SPEED_CAPABLE
+ ---help---
+ This device is capable of USB 2.0 High Speed connections.
+ This option can be used to limit connections to Full Speed
+ only. Generally this should not be neccessary.
+ endif
+
+ # Device that can be bus powered
+ if ( OTG_BUS_POWERED_CAPABLE = 'y')
+
+ config OTG_SELF_POWERED
+ bool 'Enable if device is Self-Powered'
+ default y
+ ---help---
+ This device is capable of being bus powered or self powered.
+ Enable this option if it is self powered.
+
+ if ( OTG_SELF_POWERED = 'n')
+
+ config OTG_BMAXPOWER
+ int 'bMaxPower - 2mA units'
+ default 1
+ ---help---
+ The amount of power the device will draw in 2mA units.
+
+ endif
+
+ # N.B. bMaxPower must be 1 even for self powered devices.
+ if ( OTG_SELF_POWERED != 'n')
+ config OTG_BMAXPOWER
+ int
+ default 1
+ endif
+ endif
+
+ # Device that can only be self-powered,
+ # N.B. bMaxPower must be 1 even for self powered devices.
+ if ( OTG_BUS_POWERED_CAPABLE != 'y')
+
+ config OTG_SELF_POWERED
+ bool
+ default y
+
+ config OTG_BMAXPOWER
+ int
+ default 1
+ endif
+
+ # Device that can implement Remote Wakeup
+ if ( OTG_REMOTE_WAKEUP_CAPABLE = 'y')
+
+ config OTG_REMOTE_WAKEUP
+ bool 'Enable Remote Wakeup'
+ default y
+ ---help---
+ This device can initiate a Remote Wakeup. Do you wish
+ to enable this capability. This will add the Remote Wakeup
+ bit to bmAttributes.
+
+ Note that Chapter 9 tests will require
+ that you use the usbadmin (or similiar) program to test
+ this.
+
+ endif
+
+
+ config OTG_TR_AUTO
+ bool 'Enable Auto-Start'
+ default OTG_USB_PERIPHERAL || OTG_USB_PERIPHERAL_OR_HOST || OTG_USB_HOST
+ ---help---
+ Automatically start and enable standard USB Device or USB
+ Host. If disabled, a USBOTG management application will need
+ to enable the OTG software before the device can be used.
+
+ #comment ""
+ #comment "OTG Support In Linux USB Core"
+ #
+ #config OTG_HOST
+ # boolean "USB Host - OTG Support"
+ # depends on OTG_DEVICE && USB
+ # default OTG_DEVICE
+ # ---help---
+ # This option adds OTG options in the standard Linux
+ # USB Core to support implmentation of On-The-Go Devices.
+ # This should be enabled if implementing an OTG Device.
+
+ comment ""
+ comment "Language"
+
+ config OTG_LANGID
+ hex "Language ID)"
+ depends on OTG
+ default "0x0904"
+ ---help---
+ This option sets the default language ID. Typical
+ values are 0x0904 for US English, or 0x0903 for English.
+
+
+ comment "Testing and Portability"
+
+ config OTG_TRACE
+ bool 'OTG Fast Tracing'
+ depends on OTG!=n
+ ---help---
+ This option implements register trace to support
+ driver debugging; do not enable in production devices.
+
+ config OTG_TRACE_PACKED
+ bool 'OTG Fast Tracing -- packed option'
+ depends on (OTG !=n) && (OTG_TRACE != n)
+ default n
+ ---help---
+ This option is experimental. Do not enable for normal use
+ config OTG_LATENCY_CHECK
+ bool 'OTG Latency Check'
+ depends on (OTG !=n) && (OTG_TRACE != n)
+ default n
+ ---help---
+ This option is experimental. Do not enable for normal use
+
+
+ config OTG_NOC99
+ bool 'Disable C99 initializers'
+ ---help---
+ Enable this if your compiler does not allow a structure to
+ be initialized as .element_name=value
+
+ config OTG_INTERNAL_TESTING
+ bool 'Enable internal testing modes'
+ depends on (OTG != n)
+ default n
+ ---help---
+ This option enables Belcara internal testing options.
+ Do not enable for normal use.
+
+ endmenu
diff --git a/drivers/otg/Makefile b/drivers/otg/Makefile
new file mode 100644
index 000000000000..c029a1f545bd
--- /dev/null
+++ b/drivers/otg/Makefile
@@ -0,0 +1,30 @@
+#
+# Belcarra OTG - On-The-Go
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/.Makefiles/Makefile-freescale|20070530040553|49544
+#
+# Copyright (c) 2004-2005 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+TOPDIR ?= ../..
+
+obj-y +=
+obj-$(CONFIG_OTG) += otgcore/
+
+EXTRA_CFLAGS += -Wno-format -Wall
+# Function Drivers
+obj-$(CONFIG_OTG_GENERIC) += functions/generic/
+obj-$(CONFIG_OTG_ACM) += functions/acm/
+obj-$(CONFIG_OTG_MSC) += functions/msc/
+obj-$(CONFIG_OTG_MOUSE) += functions/mouse/
+obj-$(CONFIG_OTG_NETWORK) += functions/network/
+
+ifeq ($(CONFIG_OTG),m)
+obj-m += hardware/
+ifeq ($(CONFIG_OTG_NETWORK),m)
+obj-y += functions/network/
+endif
+endif
+
+ifeq ($(CONFIG_OTG),y)
+obj-y += hardware/
+endif
diff --git a/drivers/otg/functions/acm/Kconfig b/drivers/otg/functions/acm/Kconfig
new file mode 100644
index 000000000000..4c648a9ddd39
--- /dev/null
+++ b/drivers/otg/functions/acm/Kconfig
@@ -0,0 +1,19 @@
+# @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/Kconfig|20070628094342|31231
+
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+menu "OTG ACM Function"
+
+config OTG_ACM
+ tristate "CDC ACM Function"
+ depends on OTG
+ default OTG
+
+config OTG_ACM_PIPES_TEST
+ bool "Pipes Test Only"
+ depends on OTG && OTG_ACM && OTG_INTERNAL_TESTING
+ default n
+ ---help---
+ Setting this allows the pipes to be tested separately.
+
+endmenu
diff --git a/drivers/otg/functions/acm/Makefile b/drivers/otg/functions/acm/Makefile
new file mode 100644
index 000000000000..ccb7f855fea3
--- /dev/null
+++ b/drivers/otg/functions/acm/Makefile
@@ -0,0 +1,15 @@
+# Function driver for a CDC ACM OTG Device
+# @(#) balden@belcarra.com|otg/functions/acm/Makefile-l26|20060419204257|06816
+#
+# Copyright (c) 2004 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+tty_if-objs := acm-l26.o acm.o tty-l26-os.o tty-if.o
+
+obj-$(CONFIG_OTG_ACM) += tty_if.o
+
+OTG=$(TOPDIR)/drivers/otg
+ACMD=$(OTG)/functions/acm
+USBDCORE_DIR=$(OTG)/usbdcore
+EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR)
+EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR)
diff --git a/drivers/otg/functions/acm/acm-l26.c b/drivers/otg/functions/acm/acm-l26.c
new file mode 100644
index 000000000000..f445569c8846
--- /dev/null
+++ b/drivers/otg/functions/acm/acm-l26.c
@@ -0,0 +1,132 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/acm/acm-l24-os.c
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/acm-l26.c|20070911235624|22864
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/functions/acm/acm-l26.c
+ * @brief ACM Function Driver private defines
+ *
+ * This is the linux module wrapper for the tty-if function driver.
+ *
+ * TTY
+ * Interface
+ * Upper +----------+
+ * Edge | tty-l26 |
+ * Implementation +----------+
+ *
+ *
+ * Function +----------+
+ * Descriptors | tty-if |
+ * Registration +----------+
+ *
+ *
+ * Function +----------+
+ * I/O | acm |
+ * Implementation +----------+
+ *
+ *
+ * Module +----------+
+ * Loading | acm-l26 | <-----
+ * +----------+
+ *
+ * @ingroup ACMFunction
+ */
+
+
+//#include <otg/osversion.h>
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+MOD_AUTHOR ("sl@belcarra.com");
+
+MOD_DESCRIPTION ("Belcarra TTY Function");
+EMBED_LICENSE();
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/serial.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include <linux/capability.h>
+#include <otg/otg-trace.h>
+#include "acm.h"
+#include "tty.h"
+
+MOD_PARM_INT (vendor_id, "Device Vendor ID", 0);
+MOD_PARM_INT (product_id, "Device Product ID", 0);
+MOD_PARM_INT (max_queued_urbs, "Maximum TX Queued Urbs", 0);
+MOD_PARM_INT (max_queued_bytes, "Maximum TX Queued Bytes", 0);
+
+/*! acm_l26_modinit - module init
+ *
+ * This is called immediately after the module is loaded or during
+ * the kernel driver initialization if linked into the kernel.
+ *
+ */
+STATIC int acm_l26_modinit (void)
+{
+ BOOL tty_l26 = FALSE, tty_if = FALSE;
+
+ /* register tty and usb interface function drivers
+ */
+ TTY = otg_trace_obtain_tag(NULL, "acm");
+ THROW_UNLESS(tty_l26 = BOOLEAN(!tty_l26_init("tty_if", 2)), error);
+ THROW_UNLESS(tty_if = BOOLEAN(!tty_if_init()), error);
+
+ CATCH(error) {
+ if (tty_l26) tty_l26_exit();
+ if (tty_if) tty_if_exit();
+ otg_trace_invalidate_tag(TTY);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*! acm_l26_modexit - module cleanup
+ *
+ * This is called prior to the module being unloaded.
+ */
+STATIC void acm_l26_modexit (void)
+{
+ /* de-register as tty and usb drivers */
+ tty_l26_exit();
+ tty_if_exit();
+ otg_trace_invalidate_tag(TTY);
+}
+
+module_init (acm_l26_modinit);
+module_exit (acm_l26_modexit);
diff --git a/drivers/otg/functions/acm/acm.c b/drivers/otg/functions/acm/acm.c
new file mode 100644
index 000000000000..5c227800e003
--- /dev/null
+++ b/drivers/otg/functions/acm/acm.c
@@ -0,0 +1,1478 @@
+/*
+ * 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
+ */
+
+/*
+ * otg/functions/acm/acm-fd.c
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/acm.c|20070912103628|42749
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @file otg/functions/acm/acm.c
+ * @brief ACM Function Driver private defines
+ *
+ * This is the ACM Protocol specific portion of the
+ * ACM driver.
+ *
+ * TTY
+ * Interface
+ * Upper +----------+
+ * Edge | tty-l26 |
+ * Implementation +----------+
+ *
+ *
+ * Function +----------+
+ * Descriptors | tty-if |
+ * Registration +----------+
+ *
+ *
+ * Function +----------+
+ * I/O | acm | <-----
+ * Implementation +----------+
+ *
+ *
+ * Module +----------+
+ * Loading | acm-l26 |
+ * +----------+
+ *
+ * @ingroup ACMFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/wait.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-mcpc.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include "acm.h"
+
+otg_tag_t acm_trace_tag;
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! acm_urb_sent_ep0 - called to indicate ep0 URB transmit finished
+ *
+ * @param urb pointer to struct usbd_urb
+ * @param urb_rc result
+ * @return non-zero for error
+ */
+int acm_urb_sent_ep0 (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ //otg_trace_msg(TTY, __FUNCTION__,
+ // "URB SENT acm: %x urb: %x urb_rc: %d length: %d",
+ // (u32)acm, (u32)urb, urb_rc, urb->actual_length);
+
+ TRACE_MSG4(TTY,"URB SENT acm: %x urb: %x urb_rc: %d length: %d", (u32)acm, (u32)urb, urb_rc, urb->actual_length);
+
+ RETURN_EINVAL_UNLESS(acm);
+
+ TRACE_MSG1(TTY,"urb: %x", urb);
+ TRACE_MSG1(TTY, "length: %d", urb->actual_length);
+
+ urb->function_privdata = NULL;
+ usbd_free_urb(urb);
+ return 0;
+}
+
+/*! acm_line_coding_urb_received - callback for sent URB
+ * @brief Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @param urb
+ * @param urb_rc
+ * @return non-zero for failure.
+ */
+int acm_line_coding_urb_received (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ TRACE_MSG5(TTY,"URB RECEIVED acm: %x urb: %x urb_rc: %d length: %d %d", acm, urb, urb_rc,
+ urb->actual_length, sizeof(struct cdc_acm_line_coding));
+
+ RETURN_EINVAL_UNLESS(acm);
+ RETURN_EINVAL_IF (USBD_URB_OK != urb_rc);
+ RETURN_EINVAL_IF (urb->actual_length < sizeof(struct cdc_acm_line_coding));
+
+ // something changed, copy and notify
+
+ memcpy(&acm->line_coding, urb->buffer, sizeof(struct cdc_acm_line_coding));
+
+ // XXX notify application if baudrate has changed
+ // we need an os layer function to call here.... it should decode line_coding
+ // and if possible notify posix layer that things have changed.
+
+ usbd_free_urb(urb);
+
+ if (acm->ops->line_coding)
+ acm->ops->line_coding(function_instance);
+
+ return 0;
+}
+
+
+/*!
+ * @brief acm_set_link_urb_received - callback for sent URB
+ * @param urb
+ * @param urb_rc result
+ * @return non-zero for error
+ */
+int acm_set_link_urb_received (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG4(TTY,"URB RECEIVED acm: %x urb: %x urb_rc: %d length: %d", acm, urb, urb_rc, urb->actual_length);
+
+ usbd_free_urb(urb);
+ return 0;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * acm_start_out_urb - called to queue out urb on ep0 to receive device request OUT data
+ * @param function
+ * @param len
+ * @param callback
+ * @return non-zero if error
+ */
+int acm_start_out_urb_ep0(struct usbd_function_instance *function, int len, int (*callback) (struct usbd_urb *, int))
+{
+ struct acm_private *acm = function->privdata;
+ struct usbd_urb *urb;
+ RETURN_ZERO_UNLESS(len);
+ RETURN_ENOMEM_UNLESS((urb = usbd_alloc_urb_ep0(function, len, callback)));
+ urb->function_privdata = acm;
+ urb->function_instance = function;
+ RETURN_ZERO_UNLESS (usbd_start_out_urb(urb));
+ usbd_free_urb(urb); // de-alloc if error
+ return -EINVAL;
+}
+
+/*!
+ * acm_device_request - called to process a request to endpoint or interface
+ * @param function
+ * @param request
+ * @return non-zero if error
+ */
+int acm_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct acm_private *acm = interface_instance->function.privdata;
+
+ TRACE_SETUP(TTY, request);
+
+ // determine the request direction and process accordingly
+ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
+ TRACE_MSG1(TTY, "H2D CLASS bRequest: %02x", request->bRequest);
+ switch (request->bRequest) {
+ /* CDC */
+ case CDC_CLASS_REQUEST_SEND_ENCAPSULATED: break;
+ case CDC_CLASS_REQUEST_SET_COMM_FEATURE: break;
+ case CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE: break;
+
+ case CDC_CLASS_REQUEST_SET_LINE_CODING: /* 0x20 */
+ return acm_start_out_urb_ep0(function, le16_to_cpu(request->wLength), acm_line_coding_urb_received);
+
+ case CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE: { /* 0x22 */
+ unsigned int prev_bmLineState = acm->bmLineState;
+ acm->bmLineState = le16_to_cpu(request->wValue);
+
+ // schedule writers or hangup IFF open
+ TRACE_MSG5(TTY, "acm: %x interface: %x set control state, bmLineState: %04x previous: %04x changed: %04x",
+ acm, interface_instance,
+ acm->bmLineState, prev_bmLineState, acm->bmLineState ^ prev_bmLineState);
+
+ /* make sure there really is a state change in D0 */
+ if ((acm->bmLineState ^ prev_bmLineState) & CDC_LINESTATE_D0_DTR) {
+
+ TRACE_MSG1(TTY, "DTR state changed -> %x",
+ (acm->bmLineState & CDC_LINESTATE_D0_DTR));
+
+ if (acm->bmLineState & CDC_LINESTATE_D0_DTR) {
+ if (acm->flags & ACM_OPENED)
+ acm->ops->schedule_wakeup_writers(function);
+
+ //acm_schedule_recv_restart(function);
+ //acm->ops->recv_start_bh(function);
+ acm_start_recv_urbs(function);
+
+ }
+ #if 0
+ else {
+ if (acm->flags & ACM_OPENED)
+ acm->ops->schedule_hangup(function);
+
+ acm_flush(function);
+ }
+ #endif
+
+ /* wake up blocked opens */
+ //acm->ops->wakeup_opens(function);
+
+ /* wake up blocked ioctls */
+ acm->ops->wakeup_state(function);
+
+ }
+
+ /* make sure there really is a state change in D1 */
+ if ((acm->bmLineState ^ prev_bmLineState) & CDC_LINESTATE_D1_RTS) {
+
+ TRACE_MSG1(TTY, "DCD state changed -> %x",
+ (acm->bmLineState & CDC_LINESTATE_D1_RTS));
+
+ /* wake up blocked opens */
+ acm->ops->wakeup_opens(function);
+
+ /* wake up blocked ioctls */
+ acm->ops->wakeup_state(function);
+
+ }
+
+ /* check for hangup - both D0 and D1 not set */
+ if ((acm->bmLineState ^ prev_bmLineState) & (CDC_LINESTATE_D0_DTR | CDC_LINESTATE_D1_RTS)) {
+ UNLESS ((acm->bmLineState & (CDC_LINESTATE_D0_DTR | CDC_LINESTATE_D1_RTS)))
+ // XXX check clocal
+ //if (acm->flags & ACM_OPENED)
+ if ((acm->flags & ACM_OPENED) && !(acm->flags & ACM_LOCAL))
+ acm->ops->schedule_hangup(function);
+ }
+
+
+ /* send notification if we have DCD */
+ TRACE_MSG1(TTY, "bmUARTState: %04x sending (DCD|DSR) notification", acm->bmUARTState);
+
+ acm_send_int_notification(function, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ break;
+ }
+
+ case CDC_CLASS_REQUEST_SEND_BREAK: break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+
+ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
+ TRACE_MSG1(TTY, "D2H CLASS bRequest: %02x", request->bRequest);
+ switch (request->bRequest) {
+ /* CDC */
+ case CDC_CLASS_REQUEST_GET_ENCAPSULATED: break;
+ case CDC_CLASS_REQUEST_GET_COMM_FEATURE: break;
+ case CDC_CLASS_REQUEST_GET_LINE_CODING: {
+ struct usbd_urb *urb;
+
+ // XXX should check wLength
+ RETURN_ENOMEM_IF (!(urb = usbd_alloc_urb_ep0(function, sizeof(struct cdc_acm_line_coding),
+ acm_urb_sent_ep0)));
+ urb->function_instance = function;
+ urb->function_privdata = acm;
+
+ memcpy(urb->buffer, &acm->line_coding, sizeof(struct cdc_acm_line_coding));
+
+ urb->actual_length = sizeof(struct cdc_acm_line_coding);
+
+ TRACE_MSG1(TTY, "sending line coding urb: %p",(u32)(void*)urb);
+ RETURN_ZERO_UNLESS(usbd_start_in_urb(urb));
+ usbd_free_urb(urb);
+ TRACE_MSG0(TTY, "(send failed)--> -EINVAL");
+ return -EINVAL;
+ }
+ default:
+ TRACE_MSG1(TTY, "UNKNOWN D2H CLASS bRequest: %02x", request->bRequest);
+ return -EINVAL;
+ }
+ return 0;
+
+ /* MCPC */
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:
+ TRACE_MSG1(TTY, "H2D Vendor bRequest: %02x", request->bRequest);
+ switch (request->bRequest) {
+ /* MCPC */
+ case MCPC_REQ_ACTIVATE_MODE:
+ TRACE_MSG1(TTY, "H2D Vendor bRequest: %02x MCPC_REQ_ACTIVATE_MODE", request->bRequest);
+ return 0;
+
+ case MCPC_REQ_SET_LINK:
+ return acm_start_out_urb_ep0(function, le16_to_cpu(request->wLength), acm_set_link_urb_received);
+
+ case MCPC_REQ_CLEAR_LINK:
+ TRACE_MSG1(TTY, "H2D Vendor bRequest: %02x MCPC_REQ_CLEAR_LINK", request->bRequest);
+ return 0;
+
+ case MCPC_REQ_MODE_SPECIFIC_REQUEST:
+ TRACE_MSG1(TTY, "H2D Vendor bRequest: %02x MCPC_REQ_SPECIFIC_REQUEST", request->bRequest);
+ return 0;
+ default:
+ TRACE_MSG1(TTY, "UNKNOWN H2D CLASS bRequest: %02x XXXX", request->bRequest);
+ return -EINVAL;
+ }
+ break;
+
+ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR:
+ TRACE_MSG1(TTY, "D2H Vendor bRequest: %02x", request->bRequest);
+ switch (request->bRequest) {
+ case MCPC_REQ_GET_MODETABLE:
+ TRACE_MSG1(TTY, "D2H Vendor bRequest: %02x MCPC_REQ_GET_MODETABLE", request->bRequest);
+ return 0;
+ case MCPC_REQ_MODE_SPECIFIC_REQUEST:
+ TRACE_MSG1(TTY, "D2H Vendor bRequest: %02x MCPC_REQ_SPECIFIC_REQUEST", request->bRequest);
+ return 0;
+ default:
+ TRACE_MSG1(TTY, "UNKNOWN D2H Vendor bRequest: %02x", request->bRequest);
+ return -EINVAL;
+ }
+ break;
+
+ default:
+ TRACE_MSG2(TTY, "unknown bmRequestType: %02x bRequest: %02x", request->bmRequestType, request->bRequest);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*! @brief acm_endpoint_cleared - called by the USB Device Core when endpoint cleared
+ * @param function_instance The function instance for this driver
+ * @param bEndpointAddress
+ * @return none
+ */
+void acm_endpoint_cleared (struct usbd_function_instance *function_instance, int bEndpointAddress)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ TRACE_MSG1(TTY, "CLEARED bEndpointAddress: %02x", bEndpointAddress);
+}
+
+
+/* ******************************************************************************************* */
+/*! acm_open -
+ * @param function_instance
+ * @param chan
+ * @return int
+ */
+int acm_open(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_EINVAL_UNLESS(acm);
+
+ TRACE_MSG0(TTY,"OPEN");
+ acm->flags |= ACM_OPENED;
+ // XXX config option for old behavior
+ acm->bmUARTState = /* CDC_UARTSTATE_BRXCARRIER_DCD | */ CDC_UARTSTATE_BTXCARRIER_DSR;
+
+ //acm_schedule_recv_restart(function_instance);
+ // acm->ops->recv_start_bh(function_instance);
+ acm_start_recv_urbs(function_instance);
+
+ TRACE_MSG1(TTY,"bmUARTState: %04x", acm->bmUARTState);
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ return 0;
+}
+
+/*! acm_flush
+ * @brief flush BLUK_IN, BLUK_OUT endpoints
+ * @param function_instance
+ * @return
+ */
+void acm_flush(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+
+ RETURN_UNLESS(acm);
+ atomic_set(&acm->bytes_received, 0);
+ atomic_set(&acm->bytes_forwarded, 0);
+ // XXX config option for old behavior
+ acm->bmUARTState = /* CDC_UARTSTATE_BRXCARRIER_DCD |*/ CDC_UARTSTATE_BTXCARRIER_DSR;
+ TRACE_MSG1(TTY,"bmUARTState: %04x", acm->bmUARTState);
+
+ acm->line_coding.dwDTERate = cpu_to_le32(0x1c200); // 115200
+ acm->line_coding.bDataBits = 0x08;
+
+ RETURN_IF(usbd_get_device_status(function_instance) == USBD_CLOSING);
+ RETURN_IF(usbd_get_device_status(function_instance) != USBD_OK);
+ RETURN_IF(usbd_get_device_state(function_instance) != STATE_CONFIGURED);
+ usbd_flush_endpoint_index(function_instance, BULK_IN);
+ usbd_flush_endpoint_index(function_instance, BULK_OUT);
+}
+
+/*!acm_close
+ * @brief flush queued urbs and close connection
+ * @param function_instance
+ * @param chan
+ * @return 0 for success, EINVAL for error
+ */
+int acm_close(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ unsigned long flags;
+
+ TRACE_MSG1(TTY,"acm: %x XXXXXX", (int)acm);
+ RETURN_EINVAL_UNLESS(acm);
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+ }
+
+ otg_pthread_mutex_lock(&acm->mutex);
+
+ acm->flags &= ~ACM_OPENED;
+
+ //TRACE_MSG1(TTY,"queued: %d", (u32)(acm->queued_urbs));
+
+ if (atomic_read(&acm->queued_urbs)) {
+ acm->flags |= ACM_CLOSING;
+ TRACE_MSG1(TTY, "setting ACM_CLOSING flags: %04x", acm->flags);
+ }
+
+ otg_pthread_mutex_unlock(&acm->mutex);
+
+ /* XXX Should we wait here for data to flush...
+ */
+ RETURN_ZERO_IF(acm->flags & ACM_CLOSING);
+
+ TRACE_MSG1(TTY, "normal close flags: %04x", acm->flags);
+
+ /* Normal Close - no data pending
+ */
+ acm_flush(function_instance);
+ acm->bmUARTState = 0;
+ TRACE_MSG1(TTY,"bmUARTState: %04x", acm->bmUARTState);
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ return 0;
+}
+
+
+/*! acm_ready()
+ * @param function_instance
+ */
+STATIC int acm_ready(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ RETURN_ZERO_UNLESS(acm);
+
+ TRACE_MSG6(TTY,"FLAGS: %08x CONFIGURED: %x OPENED: %x LOCAL: %x d0_dtr: %x d1_dcd: %x",
+ acm->flags,
+ BOOLEAN(acm->flags & ACM_CONFIGURED),
+ BOOLEAN(acm->flags & ACM_OPENED),
+ BOOLEAN(acm->flags & ACM_LOCAL),
+ acm->bmLineState & CDC_LINESTATE_D0_DTR,
+ acm->bmLineState & CDC_LINESTATE_D1_RTS);
+
+ return (acm->flags & ACM_CONFIGURED) &&
+ (acm->flags & ACM_OPENED) &&
+ ((acm->flags & ACM_LOCAL) ? 1 : (acm->bmLineState & CDC_LINESTATE_D1_RTS) )
+ ;
+
+}
+
+/*! acm_start_recv_urbs
+ * @brief called to receive urbs
+ * @param function_instance
+ * @return number of urbs in queue
+ */
+int acm_start_recv_urbs(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ struct usbd_urb *urb;
+ /*
+ * Queue as many receive urbs as the OS layer has room for. Return
+ * the number in the queue (may be more than we queue here).
+ */
+ // XXX unsigned long flags;
+ int num_in_queue = 0;
+ int recv_space_available;
+#if defined(CONFIG_OTG_ACM_PIPES_TEST)
+ recv_space_available=512;
+#else
+ RETURN_ZERO_UNLESS(acm);
+ RETURN_ZERO_IF((acm->flags & ACM_THROTTLED));
+
+ UNLESS (acm_ready(function_instance)) {
+ //printk(KERN_INFO"%s: NOT READY\n", __FUNCTION__);
+ TRACE_MSG0(TTY, "START RECV: NOT READY");
+ return -1;
+ }
+
+
+ /* if configured, opened, not throttled and we have carrier or local then
+ * we can queue recv urbs.
+ */
+
+
+ // TRACE_MSG2(TTY,"START RECV: recv_urbs: %d space_avail: %d", atomic_read(&acm->recv_urbs),
+ // acm->ops->recv_space_available(function_instance, data_chan));
+
+ //recv_space_available = acm->ops->recv_space_available(function_instance, data_chan);
+#endif
+
+#if 0
+ while (((atomic_read(&acm->recv_urbs) + 1) * acm->readsize) <= recv_space_available) {
+ struct usbd_urb *urb;
+
+ BREAK_IF(!(urb = usbd_alloc_urb((struct usbd_function_instance *) function_instance,
+ BULK_OUT, acm->readsize, acm_recv_urb)));
+ atomic_inc(&acm->recv_urbs);
+ urb->function_privdata = acm;
+ TRACE_MSG3(TTY, "START RECV: %d urb: %p privdata: %p", atomic_read(&acm->recv_urbs), urb,acm);
+ CONTINUE_UNLESS (usbd_start_out_urb(urb));
+ atomic_dec(&acm->recv_urbs);
+ TRACE_MSG1(TTY, "FAILED START RECV: %d", atomic_read(&acm->recv_urbs));
+ usbd_free_urb(urb);
+ break;
+ }
+
+ /* There needs to be at least one recv urb queued in order
+ * to keep driving the push to the OS layer, so return how
+ * many there are in case the OS layer must try again later.
+ */
+ num_in_queue = atomic_read(&acm->recv_urbs);
+ return num_in_queue;
+#else
+
+
+ urb = usbd_alloc_urb((struct usbd_function_instance *) function_instance,
+ BULK_OUT, 512, acm_recv_urb);
+ atomic_inc(&acm->recv_urbs);
+ urb->function_privdata = acm;
+ TRACE_MSG3(TTY, "START RECV: %d urb: %p privdata: %p", atomic_read(&acm->recv_urbs), urb,acm);
+ RETURN_ZERO_UNLESS (usbd_start_out_urb(urb));
+ atomic_dec(&acm->recv_urbs);
+ TRACE_MSG1(TTY, "FAILED START RECV: %d", atomic_read(&acm->recv_urbs));
+ usbd_free_urb(urb);
+
+ num_in_queue = atomic_read(&acm->recv_urbs);
+ return 1;
+
+#endif
+
+}
+
+
+#if 0
+/*! @brief acm_start_recv_bh()
+ * @param data
+ * @return none
+ */
+STATIC void acm_start_recv_bh(void *data)
+{
+ struct usbd_function_instance *function_instance = data;
+ RETURN_UNLESS(function_instance);
+
+ /* keep trying to start urbs until we have enough or -1 (not ready)
+ */
+ while (!acm_start_recv_urbs(function_instance)) {
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG0(TTY, "SLEEP START");
+ interruptible_sleep_on_timeout(&acm->recv_wait, 1);
+ TRACE_MSG0(TTY, "SLEEP FINISH");
+ }
+}
+
+/*! @brief acm_restart_recv()
+ * @param data
+ * @return none
+ */
+void acm_restart_recv(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ RETURN_UNLESS(function_instance);
+
+ TRACE_MSG0(TTY, "SLEEP WAKEUP");
+ wake_up_interruptible(&acm->recv_wait);
+}
+
+/*! acm_schedule_recv_restart()
+ * @param function_instance
+ */
+void acm_schedule_recv_restart(struct usbd_function_instance *function_instance)
+{
+ unsigned long flags;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ //otg_led(LED1, TRUE);
+ SET_WORK_ARG(acm->recv_bh, function_instance);
+ SCHEDULE_WORK(acm->recv_bh);
+ //otg_led(LED1, FALSE);
+}
+#endif
+
+/* Transmit INTERRUPT ************************************************************************** */
+
+/*!
+ *@brief acm_send_int_notfication()
+ *
+ * Generates a response urb on the notification (INTERRUPT) endpoint.
+ * CALLED from interrupt context.
+ * @param function_instance
+ * @param bnotification
+ * @param data
+ * @return non-zero if error
+ */
+STATIC int acm_send_int_notification(struct usbd_function_instance *function_instance, int bnotification, int data)
+{
+ struct usbd_urb *urb = NULL;
+ struct cdc_notification_descriptor *notification;
+ int rc = 0;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_EINVAL_UNLESS(acm);
+
+ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED));
+
+ do {
+ BREAK_IF(!(urb = usbd_alloc_urb((struct usbd_function_instance *)function_instance,
+ INT_IN,
+ sizeof(struct cdc_notification_descriptor),
+ acm_urb_sent_int)));
+
+ urb->function_privdata = acm;
+ urb->function_instance = function_instance;
+
+ memset(urb->buffer, 0, urb->buffer_length);
+ urb->actual_length = sizeof(struct cdc_notification_descriptor);
+
+ /* fill in notification structure */
+ notification = (struct cdc_notification_descriptor *) urb->buffer;
+
+ notification->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+ notification->bNotification = bnotification;
+
+ switch (bnotification) {
+ case CDC_NOTIFICATION_NETWORK_CONNECTION:
+ notification->wValue = data;
+ break;
+ case CDC_NOTIFICATION_SERIAL_STATE:
+ notification->wLength = cpu_to_le16(2);
+ *((unsigned short *)notification->data) = cpu_to_le16(data);
+ break;
+ }
+
+ BREAK_IF(!(rc = usbd_start_in_urb (urb)));
+
+ urb->function_privdata = NULL;
+ usbd_free_urb (urb);
+
+ TRACE_MSG1(TTY,"urb: %x", urb);
+
+ } while(0);
+
+ return rc;
+}
+
+/* Callback functions for TX urb completion (function chosen when urb is allocated) ***********/
+
+
+
+/*! acm_closing
+ */
+int acm_closing(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ RETURN_ZERO_UNLESS(acm->flags & ACM_CLOSING);
+
+ acm->flags &= ~ACM_CLOSING;
+
+ /* No more data to send - complete close
+ */
+ acm_flush(function_instance);
+ acm->bmUARTState = 0;
+ TRACE_MSG1(TTY,"bmUARTState: %04x", acm->bmUARTState);
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+
+ return 0;
+}
+
+#if 1
+/*! acm_urb_sent_zle - callback for completed BULK ZLE URB
+ *
+ * Handles notification that an ZLE urb has been sent (successfully or otherwise).
+ *
+ * @param urb Pointer to the urb that has been sent.
+ * @param urb_rc Result code from the send operation.
+ *
+ * @return non-zero for failure.
+ */
+int acm_urb_sent_zle (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ void *buf;
+ int rc = -EINVAL;
+ int in_pkt_sz;
+
+ //TRACE_MSG4(TTY,"urb: %p urb_rc: %d length: %d queue: %d",
+ // urb, urb_rc, urb->actual_length, acm->queued_urbs);
+
+ usbd_free_urb (urb);
+ rc = 0;
+ return acm_closing(function_instance);
+}
+#endif
+
+
+/*! acm_urb_sent_bulk - called to indicate bulk URB transmit finished
+ * @param urb pointer to struct usbd_urb
+ * @param rc result
+ * @return non-zero for error
+ */
+STATIC int acm_urb_sent_bulk (struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, BULK_IN, usbd_high_speed(function_instance));
+ struct usbd_urb *tx_urb;
+ static int count=1;
+
+ TRACE_MSG5(TTY,"acm: %x urb: %x length: %d flags: %x queued_bytes: %d",
+ acm, urb, urb->actual_length,
+ acm ? acm->flags : 0,
+ acm ? atomic_read(&acm->queued_bytes) : 0
+ );
+ RETURN_ZERO_UNLESS(acm);
+
+#if defined(CONFIG_OTG_ACM_PIPES_TEST)
+ RETURN_EINVAL_UNLESS(tx_urb = usbd_alloc_urb ((struct usbd_function_instance *) function_instance,
+ BULK_IN, count+1, acm_urb_sent_bulk));
+
+ memset ((void *)tx_urb->buffer, count & 0xff, count);
+ tx_urb->function_privdata = acm;
+ tx_urb->actual_length = count;
+ if (count <1024) count+=1;
+ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
+ tx_urb->function_privdata = NULL;
+ usbd_free_urb (tx_urb);
+ return 0;
+
+#else
+
+ atomic_sub(urb->actual_length, &acm->queued_bytes);
+ atomic_dec(&acm->queued_urbs);
+
+ TRACE_MSG3(TTY, "queued_bytes: %d queude_urbs: %d opened: %d",
+ atomic_read(&acm->queued_bytes), atomic_read(&acm->queued_urbs), BOOLEAN(acm->flags & ACM_OPENED));
+
+ urb->function_privdata = NULL;
+ usbd_free_urb (urb);
+
+ /* wakeup upper layers to send more data */
+ if (acm->flags & ACM_OPENED)
+ acm->ops->schedule_wakeup_writers(function_instance);
+
+ /* check if there are still pending send urbs */
+ RETURN_ZERO_IF(atomic_read(&acm->queued_urbs));
+
+ /* check if we should send a zlp, send ZLP */
+ #if 1
+ UNLESS (urb->actual_length % in_pkt_sz) {
+ if ((urb = usbd_alloc_urb (function_instance, BULK_IN, 0, acm_urb_sent_zle ))) {
+ urb->actual_length = 0;
+ TRACE_MSG1(TTY, "Sending ZLP: length: %d", urb->actual_length);
+ urb->flags |= USBD_URB_SENDZLP;
+
+ if ((rc = usbd_start_in_urb (urb))) {
+ TRACE_MSG1(TTY,"FAILED: %d", rc);
+ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+ urb->function_privdata = NULL;
+ usbd_free_urb (urb);
+ }
+ }
+ return 0;
+ }
+ #endif
+ return acm_closing(function_instance);
+#endif
+}
+
+/*! acm_urb_sent_int - called to indicate int URB transmit finished
+ * @param urb pointer to struct usbd_urb
+ * @param rc result
+ * @return non-zero for error
+ */
+int acm_urb_sent_int (struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+
+ TRACE_MSG1(TTY,"urb: %x", urb);
+ TRACE_MSG1(TTY,"INT length=%d",urb->actual_length);
+
+ urb->function_privdata = NULL;
+ usbd_free_urb(urb);
+ return 0;
+}
+
+/* USB Device Functions ************************************************************************ */
+
+
+/*!acm_write_room
+ * @brief get available room for queuing receiving urbs
+ * @param function_instance
+ * @param chan
+ * @return non-zero if error
+ */
+STATIC int acm_write_room(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ TRACE_MSG2(TTY,"acm: %x queued_bytes: %d", acm, acm ? atomic_read(&acm->queued_bytes) : 0);
+ RETURN_ZERO_UNLESS(acm);
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+ }
+
+ return (acm->max_queued_urbs <= atomic_read(&acm->queued_urbs) ||
+ acm->max_queued_bytes <= atomic_read(&acm->queued_bytes) ) ? 0 : acm->writesize ;
+}
+
+/*!acm_chars_in_buffer
+ * @brief get number of of queued chars
+ * @param function_instance
+ * @param chan
+ * @return number of chars, zero if error
+ */
+STATIC int acm_chars_in_buffer(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ int rc;
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ TRACE_MSG2(TTY,"acm: %x queued_bytes: %d", (int)acm, acm ? atomic_read(&acm->queued_bytes) : 0);
+ RETURN_ZERO_UNLESS(acm);
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+ }
+
+ return atomic_read(&acm->queued_bytes);
+}
+
+static int throttle_count = 0;
+static int unthrottle_count = 0;
+
+
+/*! acm_xmit_chars
+ * @brief - transmit data to host
+ * @param function_instance
+ * @param chan
+ * @param count
+ * @param from_user
+ * @param buf
+ * @return number of bytes sent.
+ */
+int acm_xmit_chars(struct usbd_function_instance *function_instance, minor_chan_t chan,
+ int count, int from_user, const unsigned char *buf)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ struct usbd_urb *urb;
+ int in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, BULK_IN, usbd_high_speed(function_instance));
+ int rc=0;
+
+ TRACE_MSG4(TTY,"acm[%d]: %x flags: %x queued_bytes: %d", chan, acm,
+ acm ? acm->flags: 0, acm ? atomic_read(&acm->queued_bytes) : 0);
+ RETURN_ZERO_UNLESS(acm);
+ RETURN_ZERO_UNLESS((acm->flags & ACM_CONFIGURED) &&
+ ((acm->flags & ACM_LOCAL) ? 1 : (acm->bmLineState & CDC_LINESTATE_D1_RTS)));
+
+ //TRACE_MSG5(TTY, "count: %d from_user: %d queued: %d max: %d writesize: %d",
+ // count, from_user, acm->queued_urbs, acm->max_queued_urbs, acm->writesize);
+
+ switch(chan) {
+ case data_chan:
+ // sanity check and are we connect
+ // XXX RETURN_ZERO_UNLESS(atomic_read(&acm->used));
+ TRACE_MSG0(TTY,"DATA");
+ RETURN_ZERO_UNLESS (count);
+ RETURN_ZERO_UNLESS (acm->flags & ACM_CONFIGURED); // XXX acm_ready() ??
+ TRACE_MSG0(TTY,"connected OK");
+ RETURN_ZERO_IF(acm->max_queued_urbs <= atomic_read(&acm->queued_urbs));
+ TRACE_MSG0(TTY,"acm->max_queued_urbs OK");
+
+ // allocate a write urb
+ count = MIN(count, acm->writesize);
+
+ RETURN_ZERO_UNLESS ((urb = usbd_alloc_urb ((struct usbd_function_instance *) function_instance,
+ BULK_IN, count, acm_urb_sent_bulk)));
+
+ if (from_user)
+ rc=copy_from_user ((void *)urb->buffer, (void *)buf, count);
+ else
+ memcpy ((void *)urb->buffer, (void *)buf, count);
+
+
+ #if 0
+ {
+ u8 *cp = urb->buffer;
+ int len;
+ for (len = 0; len < count; len += 8, cp += 8) {
+ TRACE_MSG8(TTY, "SEND [%02x %02x %02x %02x %02x %02x %02x %02x]",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ }
+ }
+ #endif
+
+ urb->function_privdata = acm;
+ urb->actual_length = count;
+
+ #if 0
+ /* force ZLP if multiple of packet size */
+ UNLESS(count % in_pkt_sz) {
+ urb->flags |= USBD_URB_SENDZLP;
+ }
+ #endif
+
+ atomic_add(count, &acm->queued_bytes);
+ atomic_inc(&acm->queued_urbs);
+
+ UNLESS (usbd_start_in_urb (urb)) return count;
+
+ usbd_free_urb (urb);
+ atomic_sub(count, &acm->queued_bytes);
+ atomic_dec(&acm->queued_urbs);
+ return 0;
+
+ case command_chan:
+ TRACE_MSG0(TTY,"COMMAND");
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*! acm_recv_urb - callback for urb received
+ * @param urb
+ * @param rc
+ * @return non-zero if error
+ */
+int acm_recv_urb (struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct acm_private *acm = function_instance->privdata;
+
+ /* Return 0 if urb has been accepted,
+ * return 1 and expect caller to deal with urb release otherwise.
+ */
+
+ TRACE_MSG2(TTY, "acm: %x len: %d", acm, urb->actual_length);
+
+ atomic_dec(&acm->recv_urbs); // this could probably be atomic operation....
+
+ if (USBD_URB_CANCELLED == rc) {
+ TRACE_MSG1(TTY,"cancelled URB=%p",urb);
+ return -EINVAL;
+ }
+ if (USBD_URB_OK != rc) {
+ TRACE_MSG2(TTY,"rejected URB=%p rc=%d",urb,rc);
+ usbd_free_urb(urb);
+
+ //acm_schedule_recv_restart(function_instance);
+ // acm->ops->recv_start_bh(function_instance);
+
+ acm_start_recv_urbs(function_instance);
+ return 0;
+ }
+#if defined(CONFIG_OTG_ACM_PIPES_TEST)
+ acm_start_recv_urbs(function_instance);
+
+#else
+ /* loopback mode
+ */
+ if (acm->flags & ACM_LOOPBACK)
+ acm_xmit_chars(function_instance, data_chan, urb->actual_length, 0, urb->buffer);
+
+ /* acm_start_recv_urbs() will never queue more urbs than there is currently
+ * room in the upper layer buffer for. So we are guaranteed that any data actually
+ * received can be given to the upper layers without worrying if we will
+ * actually have room.
+ */
+ else
+ if ((rc = acm->ops->recv_chars(function_instance, urb->buffer, urb->actual_length)))
+ return rc; // XXX
+
+
+ atomic_add(urb->actual_length, &acm->bytes_received);
+
+ TRACE_MSG1(TTY,"bytes_received: %d", atomic_read(&acm->bytes_received));
+ usbd_free_urb(urb);
+
+ //acm_schedule_recv_restart(function_instance);
+ // acm->ops->recv_start_bh(function_instance);
+ acm_start_recv_urbs(function_instance);
+ return 0;
+#endif
+}
+
+/*! acm_wait_task - delay task to perform
+ * @param function_instance
+ * @param queue
+ */
+void acm_wait_task(struct usbd_function_instance *function_instance, OLD_WORK_ITEM *queue)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+
+
+}
+
+/* ********************************************************************************************* */
+/*! acm_get_d0_dtr
+ * @brief get d0 DTR status
+ * Get DTR status.
+ * @param function_instance
+ * @return int
+ */
+int acm_get_d0_dtr(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+ TRACE_MSG2(TTY,"bmLineState: %04x d0_dtr: %x", acm->bmLineState, (acm->bmLineState & CDC_LINESTATE_D0_DTR) ? 1 : 0);
+ return (acm->bmLineState & CDC_LINESTATE_D0_DTR) ? 1 : 0;
+}
+
+/*!
+ * @brief acm_get_d1_rts
+ * Get DSR status
+ * @param function_instance
+ * @return int
+ */
+int acm_get_d1_rts(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+ TRACE_MSG2(TTY,"bmLineState: %04x d1_rts: %x", acm->bmLineState, (acm->bmLineState & CDC_LINESTATE_D1_RTS) ? 1 : 0);
+ return (acm->bmLineState & CDC_LINESTATE_D1_RTS) ? 1 : 0;
+}
+
+
+/* ********************************************************************************************* */
+/*!
+ * @brief acm_get_bRxCarrier
+ * Get bRxCarrier (DCD) status
+ * @param function_instance
+ @ @return int
+ */
+int acm_get_bRxCarrier(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+ TRACE_MSG2(TTY,"bmLineState: %04x bRxCarrier: %x",
+ acm->bmLineState, (acm->bmUARTState & CDC_UARTSTATE_BRXCARRIER_DCD) ? 1 : 0);
+ return (acm->bmUARTState & CDC_UARTSTATE_BRXCARRIER_DCD) ? 1 : 0;
+}
+
+/*!
+ * @brief acm_get_bTxCarrier
+ * Get bTxCarrier (DSR) status
+ * @param function_instance
+ @ @return int
+ */
+int acm_get_bTxCarrier(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+ TRACE_MSG2(TTY,"bmLineState: %04x bTxCarrier: %x",
+ acm->bmLineState, (acm->bmUARTState & CDC_UARTSTATE_BTXCARRIER_DSR) ? 1 : 0);
+ return (acm->bmUARTState & CDC_UARTSTATE_BTXCARRIER_DSR) ? 1 : 0;
+}
+
+
+/*! @brief acm_set_bRxCarrier
+ * Get bRxCarrier (DCD) status
+ * @param function_instance
+ * @param value
+ * @return none
+ */
+void acm_set_bRxCarrier(struct usbd_function_instance *function_instance, int value)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG2(TTY,"acm: %x value: %x", (int)acm, value);
+ RETURN_UNLESS(acm);
+ acm->bmUARTState &= ~CDC_UARTSTATE_BRXCARRIER_DCD;
+ acm->bmUARTState |= value ? CDC_UARTSTATE_BRXCARRIER_DCD : 0;
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+}
+
+/*! @brief acm_set_bTxCarrier
+ * Set bTxCarrier (DSR) status
+ * @param function_instance
+ * @param value
+ * @return none
+ */
+void acm_set_bTxCarrier(struct usbd_function_instance *function_instance, int value)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG2(TTY,"acm: %x value: %x", (int)acm, value);
+ RETURN_UNLESS(acm);
+ acm->bmUARTState &= ~CDC_UARTSTATE_BTXCARRIER_DSR;
+ acm->bmUARTState |= value ? CDC_UARTSTATE_BTXCARRIER_DSR : 0;
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+}
+
+
+/* ********************************************************************************************* */
+
+/*! acm_bRingSignal
+ * Indicate Ring signal to host.
+ */
+void acm_bRingSignal(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ acm->bmUARTState |= CDC_UARTSTATE_BRINGSIGNAL;
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ acm->bmUARTState &= ~CDC_UARTSTATE_BRINGSIGNAL;
+}
+
+/*! acm_bBreak
+ * Indicate Break signal to host.
+ */
+void acm_bBreak(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ acm->bmUARTState |= CDC_UARTSTATE_BBREAK;
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ acm->bmUARTState &= ~CDC_UARTSTATE_BBREAK;
+}
+
+/*! acm_bOverrun
+ * Indicate Overrun signal to host.
+ */
+void acm_bOverrun(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ acm->bmUARTState |= CDC_UARTSTATE_BOVERRUN;
+ acm_send_int_notification(function_instance, CDC_NOTIFICATION_SERIAL_STATE, acm->bmUARTState);
+ acm->bmUARTState &= ~CDC_UARTSTATE_BOVERRUN;
+}
+
+/*! acm_set_local
+ * Set LOCAL status
+ * @param function_instance
+ * @param value
+ */
+void acm_set_local(struct usbd_function_instance *function_instance, int value)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ TRACE_MSG2(TTY,"acm: %x value: %x", acm, value);
+ RETURN_UNLESS(acm);
+ acm->flags &= ~ACM_LOCAL;
+ acm->flags |= value ? ACM_LOCAL : 0;
+ acm_start_recv_urbs(function_instance);
+}
+
+/*! @ brief acm_set_loopback
+ * Set LOOPBACK status
+ * @param function_instance
+ * @param value
+ */
+void acm_set_loopback(struct usbd_function_instance *function_instance, int value)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG2(TTY,"acm: %x value: %d", (int)acm, value);
+ RETURN_UNLESS(acm);
+ acm->flags &= ~ACM_LOOPBACK;
+ acm->flags |= value ? ACM_LOOPBACK : 0;
+ acm_start_recv_urbs(function_instance);
+}
+
+
+/*! @ brief acm_set_throttle
+ * Set LOOPBACK status
+ * @param function_instance
+ * @param value
+ */
+void acm_set_throttle(struct usbd_function_instance *function_instance, int value)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG2(TTY,"acm: %x value: %d", (int)acm, value);
+ RETURN_UNLESS(acm);
+ acm->flags &= ~ACM_THROTTLED;
+ acm->flags |= value ? ACM_THROTTLED : 0;
+ acm_start_recv_urbs(function_instance);
+}
+
+/*! @ brief acm_get_throttled
+ * Set LOOPBACK status
+ * @param function_instance
+ */
+int acm_get_throttled(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+
+ return (acm->flags & ACM_THROTTLED) ? 1 : 0;
+}
+
+
+void acm_throttle (struct usbd_function_instance *function_instance, minor_chan_t xx){
+
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"sos acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ acm->flags |= ACM_THROTTLED;
+ usbd_flush_endpoint_index(function_instance,BULK_OUT);
+ TRACE_MSG1(TTY,"sos acm111: %x", (int)acm);
+}
+
+void acm_unthrottle (struct usbd_function_instance *function_instance, minor_chan_t xx)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ RETURN_UNLESS(acm);
+ acm->flags &= ~ACM_THROTTLED;
+ acm_start_recv_urbs(function_instance);
+
+}
+
+/*! @brief acm_function_enable - called by USB Device Core to enable the driver
+ * @param function_instance The function instance for this driver to use.
+ * @param tty_type
+ * @param os_ops
+ * @param max_urbs
+ * @param max_bytes
+ * @return non-zero if error.
+ */
+int acm_function_enable (struct usbd_function_instance *function_instance, tty_type_t tty_type,
+ struct acm_os_ops *os_ops, int max_urbs, int max_bytes)
+{
+ struct acm_private *acm = NULL;
+
+ RETURN_EINVAL_UNLESS((acm = CKMALLOC(sizeof(struct acm_private))));
+ printk(KERN_INFO "acm_function_enable and acm = CKMALLOC()\n");
+ function_instance->privdata = acm;
+
+ acm->tty_type = tty_type;
+ acm->ops = os_ops;
+ acm->max_queued_urbs = max_urbs;
+ acm->max_queued_bytes = max_bytes;
+
+ switch(tty_type) {
+ case tty_if:
+ case dun_if:
+ acm->minors = 2;
+ break;
+ default:
+ acm->minors = 1;
+ break;
+ }
+ acm->line_coding.dwDTERate = __constant_cpu_to_le32(0x1c200);
+ acm->line_coding.bDataBits = 0x08;
+ //PREPARE_WORK_ITEM(acm->recv_bh, acm_start_recv_bh, function_instance);
+
+ init_waitqueue_head(&acm->recv_wait);
+
+ TRACE_MSG1(TTY, "ENABLED writesize: %d", acm->writesize);
+
+ return acm->ops->enable(function_instance);
+}
+
+/*! @brief acm_function_disable - called by the USB Device Core to disable the driver
+ * @param function The function instance for this driver
+ */
+void acm_function_disable (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct acm_private *acm = function ? function->privdata : NULL;
+
+ TRACE_MSG0(TTY, "DISABLED");
+ acm->ops->disable(function);
+ function->privdata = NULL;
+ printk(KERN_INFO "acm_function_disable and LKFREE(acm)\n");
+ LKFREE(acm);
+}
+
+/*! acm_set_configuration
+ * @brief called to configure this function instance
+ * @param function_instance
+ * @param configuration index to set
+ * @return int
+ */
+int acm_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+ struct usbd_urb *tx_urb;
+
+ TRACE_MSG4(TTY, "CONFIGURED acm: %x wIndex:%d %x cfg: %d ", acm,
+ interface_instance->wIndex, function_instance, configuration);
+
+ // XXX Need to differentiate between non-zero, zero and non-zero done twice
+
+ /* speead and size cannot be set until now as we don't know waht speed we are
+ * enumerated at
+ */
+ acm->hs = usbd_high_speed((struct usbd_function_instance *) function_instance);
+ acm->readsize = usbd_endpoint_transferSize(function_instance, BULK_OUT, acm->hs);
+ acm->writesize = usbd_endpoint_transferSize(function_instance, BULK_IN, acm->hs);
+ acm->flags |= ACM_CONFIGURED;
+
+ TRACE_MSG2(TTY, "configured readsize: %d writesize: %d", acm->readsize, acm->writesize);
+
+#if defined(CONFIG_OTG_ACM_PIPES_TEST)
+
+ acm_start_recv_urbs(function_instance);
+
+ RETURN_EINVAL_UNLESS(tx_urb = usbd_alloc_urb ((struct usbd_function_instance *) function_instance,
+ BULK_IN, 1, acm_urb_sent_bulk));
+
+ memset ((void *)tx_urb->buffer, 1, 1);
+ tx_urb->function_privdata = acm;
+ tx_urb->actual_length = 1;
+
+ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
+ usbd_free_urb (tx_urb);
+ return -EINVAL;
+
+#else /*defined(CONFIG_OTG_MSC_PIPES_TEST) */
+ acm_start_recv_urbs(function_instance);
+#endif
+
+
+
+ return 0;
+}
+
+/*!acm_set_interface
+ * @brief set interface from function instance
+ * @param function_instance
+ * @param wIndex
+ * @param altsetting
+ * @return int
+ */
+int acm_set_interface (struct usbd_function_instance *function_instance, int wIndex, int altsetting)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+ TRACE_MSG0(TTY, "SET INTERFACE");
+ return 0;
+}
+
+/*!
+ * @brief acm_reset - called to indicate bus reset
+ * @param function_instance
+ * @return int
+ */
+int acm_reset (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+
+ TRACE_MSG0(TTY, "RESET");
+
+ /* if configured and open then schedule hangup
+ */
+ if ((acm->flags & ACM_OPENED) && (acm->flags & ACM_CONFIGURED && !(acm->flags & ACM_LOCAL)))
+ acm->ops->schedule_hangup(function_instance);
+
+ acm->flags &= ~ACM_CONFIGURED;
+ TRACE_MSG0(TTY, "RESET continue");
+
+ // XXX flush
+ // Release any queued urbs
+ return 0;
+}
+
+/*!
+ * @brief acm_suspended - called to indicate urb has been received
+ * @param function_instance
+ * @return int
+ */
+int acm_suspended (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+ TRACE_MSG0(TTY, "SUSPENDED");
+ return 0;
+}
+
+/*!
+ * acm_resumed - called to indicate urb has been received
+ * @param function_instance the function instance for this driver
+ * @return int
+ */
+int acm_resumed (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct acm_private *acm = function_instance->privdata;
+ TRACE_MSG0(TTY, "RESUMED");
+ return 0;
+}
+
+/*! acm_send_queued
+ * @brief to check how many urbs in queue waiting to send
+ * @param function_instance The function instance for this driver
+ * @return number of queued urbs
+ */
+int acm_send_queued (struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance->privdata;
+ return atomic_read(&acm->queued_urbs);
+}
diff --git a/drivers/otg/functions/acm/acm.h b/drivers/otg/functions/acm/acm.h
new file mode 100644
index 000000000000..0cf333b4e5c4
--- /dev/null
+++ b/drivers/otg/functions/acm/acm.h
@@ -0,0 +1,510 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/acm/acm.h
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/acm.h|20070911235624|28546
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup ACMFunction Serial ACM Interface Function
+ * @ingroup InterfaceFunctions
+ */
+
+/*!
+ * @file otg/functions/acm/acm.h
+ * @brief ACM Function Driver private defines
+ *
+ * This is an ACM Function Driver. The upper edge is exposed
+ * to the hosting OS as a Posix type character device. The lower
+ * edge implements the USB Device Stack API.
+ *
+ * This driver implements the CDC ACM driver model and uses the CDC ACM
+ * protocols.
+ *
+ * Note that it appears to be impossible to determine the end of a receive
+ * bulk transfer larger than wMaxPacketsize. The host is free to send
+ * wMaxPacketsize chars in a single transfer. This means that we cannot
+ * queue receive urbs larger than wMaxPacketsize (typically 64 bytes.)
+ *
+ * This does not however prevent queuing transmit urbs with larger amounts
+ * of data. It is interpreted at the receiving (host) end as a series of
+ * wMaxPacketsize transfers but because there is no interpretation to the
+ * amounts of data sent it does affect anything if we treat the data as a
+ * single larger transfer.
+ *
+ * @ingroup ACMFunction
+ */
+
+
+// Endpoint indexes in acm_endpoint_requests[] and the endpoint map.
+#define INT_IN 0x00
+#define BULK_IN 0x01
+#define BULK_OUT 0x02
+#define ENDPOINTS 0x03
+
+#define COMM_INTF 0x00
+#define DATA_INTF 0x01
+
+
+
+// Most hosts don't care about BMAXPOWER, but the UUT tests want it to be 1
+#define BMAXPOWER 1
+#define BMATTRIBUTE 0
+
+#define STATIC
+//#define STATIC static
+//
+
+/*! @name ACM Flags
+ * @{
+ */
+#define ACM_OPENED (1 << 0) /*!< upper layer has opened the device port */
+#define ACM_CONFIGURED (1 << 1) /*!< device is configured */
+#define ACM_THROTTLED (1 << 2) /*!< upper layer doesn't want to receive data */
+//#define ACM_CARRIER (1 << 3) /*!< host has set DTR, i.e. host has opened com device */
+#define ACM_LOOPBACK (1 << 4) /*!< upper layer wants local loopback */
+#define ACM_LOCAL (1 << 5) /*!< upper layer specified LOCAL mode */
+
+#define ACM_CLOSING (1 << 6) /*!< upper layer has asked us to close */
+
+/*! @} */
+
+/*! @name tty type
+ * @{
+ */
+/*! @var typedef enum tty_type tty_type_t
+ */
+typedef enum tty_type {
+ tty_if,
+ dun_if,
+ obex_if,
+ atcom_if,
+} tty_type_t;
+
+
+/*! @} */
+
+/*! @name data channel
+ * @{
+ */
+typedef enum minor_chan {
+ data_chan,
+ command_chan,
+} minor_chan_t;
+
+#define CHAN(x) ((x&1)?command_chan : data_chan)
+
+/*! @} */
+
+/*! @struct acm_os_ops acm.h "otg/functions/acm/acm.h"
+ *
+ * @brief This structure lists the operations implemented in the upper os layer.
+ */
+struct acm_os_ops {
+ /*! enable
+ * Enable the function driver.
+ */
+ int (*enable)(struct usbd_function_instance *);
+
+ /*! disable
+ * Disable the function driver.
+ */
+ void (*disable)(struct usbd_function_instance *);
+
+ /*! wakeup_opens
+ * Wakeup processes waiting for DTR.
+ */
+ void (*wakeup_opens)(struct usbd_function_instance *);
+
+ /*! wakeup_opens
+ * Wakeup processes waiting for state change.
+ */
+ void (*wakeup_state)(struct usbd_function_instance *);
+
+ /*! wakeup writers
+ * Wakeup pending writes.
+ */
+ void (*schedule_wakeup_writers)(struct usbd_function_instance *);
+
+ /*! recv_space_available
+ * Check for amount of receive space that is available, controls
+ * amount of receive urbs that will be queued.
+ */
+ int (*recv_space_available)(struct usbd_function_instance *, minor_chan_t);
+
+ /*! recv_chars
+ * Process chars received on specified interface.
+ */
+ int (*recv_chars)(struct usbd_function_instance *, u8 *cp, int n);
+
+
+ /*! schedule_hangup
+ * Schedule a work item that will perform a hangup.
+ */
+ void (*schedule_hangup)(struct usbd_function_instance *);
+
+ /*! comm_feature
+ * Tell function that comm feature has changed.
+ */
+ //void (*comm_feature)(struct usbd_function_instance *);
+
+ /*! line_coding
+ * Tell function that line coding has changed.
+ */
+ void (*line_coding)(struct usbd_function_instance *);
+
+ /*! control_state
+ * Tell function that control state has changed.
+ */
+ //void (*control_state)(struct usbd_function_instance *);
+
+ /*! send_break
+ * Tell function to send a break signal.
+ */
+ void (*send_break)(struct usbd_function_instance *);
+
+ /*! schedule_recv_start_bh
+ * Tell function to send a break signal.
+ */
+ void (*recv_start_bh)(struct usbd_function_instance *);
+
+};
+
+
+/*! @struct acm_private acm.h "otg/functions/acm/acm.h"
+ *
+ * @brief encapsulation of acm instance global variables
+ *
+ */
+struct acm_private {
+ struct usbd_function_instance *function_instance;
+ otg_tag_t trace_tag;
+
+ tty_type_t tty_type;
+
+ int index;
+ int minors;
+
+ u32 flags;
+
+ otg_pthread_mutex_t mutex;
+ otg_atomic_t recv_urbs;
+ //otg_atomic_t used;
+ otg_atomic_t queued_bytes;
+ otg_atomic_t queued_urbs;
+
+ BOOL hs;
+ int writesize;
+ int readsize;
+
+ /*TBR debug receive flow control */
+ otg_atomic_t bytes_received;
+ otg_atomic_t bytes_forwarded;
+ /*TBR end debug */
+
+ struct cdc_acm_line_coding line_coding; // C.f. Table 50 - [GS]ET_LINE_CODING Device Request (to/from host)
+ cdc_acm_bmUARTState bmUARTState; // C.f. Table 51 - SET_CONTROL_LINE_STATE Device Request (from host)
+ cdc_acm_bmLineState bmLineState; // C.f. Table 69 - SERIAL_STATE Notification (to host)
+
+ /*for tiocmget and tiocmset functions */
+ int msr;
+ int mcr;
+
+ int max_queued_urbs;
+ int max_queued_bytes;
+
+
+ OLD_WORK_STRUCT recv_bh;
+ wait_queue_head_t recv_wait; /*! wait queue for blocking open*/
+
+
+ //void *privdata;
+
+ struct acm_os_ops *ops;
+
+
+ //mcpc_mode_t mcpc_mode;
+ u8 mode; // mode if mcpc_activated
+
+ u8 modes; // sizeof received modetable
+ u8 *modetable; // most recent received mode table
+
+};
+
+
+#define TTY acm_trace_tag
+extern otg_tag_t acm_trace_tag;
+
+
+/*! acm_fd_init - called to initialize the acm_fd library
+ * This call initializs the acm_fd library and registers the function driver
+ * with the USB Peripheral Stack.
+ *
+ */
+int acm_fd_init (struct usbd_function_instance *, char *inf, u32 vendor_id, u32 product_id, u32 wmax_urbs, u32 wmax_bytes);
+
+/*! acm_fd_exit - called before exiting
+ * This call will cause the function driver to be de-registered.
+ */
+void acm_fd_exit (struct usbd_function_instance *);
+
+/*! acm_open - device open
+ * This is called the device is opened (first open only if non-exclusive opens allowed).
+ */
+int acm_open (struct usbd_function_instance *, minor_chan_t chan);
+
+/*! acm_close - Device close
+ * This is called when the device closed (last close only if non-exclusive opens allowed.)
+ */
+int acm_close (struct usbd_function_instance *, minor_chan_t chan);
+
+
+/*! acm_flush - flush data urbs.
+ * Cancel outstanding data urbs.
+ */
+void acm_flush (struct usbd_function_instance *);
+
+ /*! acm_schedule_recv - queue as many data receive urbs as possible
+ * This will schedule a bottom half hander that will will start as
+ * many receive data urbs as are allowed given the amount of room
+ * available in the upper layer. If no urbs are queued by the
+ * bottom half handler it will re-schedule itself.
+ */
+void acm_schedule_recv_restart (struct usbd_function_instance *);
+
+ /*! acm_throttle - set throttle flag for specified interface
+ * Receive urbs will not be queued when throttled.
+ */
+void acm_throttle (struct usbd_function_instance *, minor_chan_t);
+
+/*! acm_unthrottle - reset throttle flag for specified interface
+ * Receive urbs are allowed to be queued. If no urbs are queued a
+ * bottom half handler will be scheduled to queue them.
+ */
+ void acm_unthrottle (struct usbd_function_instance *, minor_chan_t);
+
+
+/*!acm_xmit_chars - send data via specified interface
+ * This will start a transmit urb to send the specified data. The
+ * number of characters sent will be returned.
+ */
+int acm_xmit_chars (struct usbd_function_instance *, minor_chan_t chan, int count, int from_user, const unsigned char *buf);
+
+/*! acm_write_room
+ * Return amount of data that could be queued for sending.
+ */
+ int acm_write_room (struct usbd_function_instance *, minor_chan_t);
+
+/*! acm_chars_in_buffer
+ * Return number of chars in xmit buffer.
+ */
+int acm_chars_in_buffer (struct usbd_function_instance *, minor_chan_t);
+
+
+/*! acm_send_int_notification - send notification via interrupt endpoint
+ * This can be used to queue network, serial state change notifications.
+ */
+ int acm_send_int_notification (struct usbd_function_instance *, int bnotification, int data);
+
+/*! acm_wait_task - wait for task to complete.
+ */
+void acm_wait_task (struct usbd_function_instance *, OLD_WORK_ITEM *queue);
+//void acm_wait_task (struct usbd_function_instance *, struct otg_workitem *work);
+/*! acm_ready - return true if connected and carrier
+ */
+int acm_ready (struct usbd_function_instance *);
+
+/*! acm_get_d1_rts
+ * Get DSR status
+ */
+int acm_get_d1_rts (struct usbd_function_instance *);
+
+/*! acm_get_d0_dtr
+ * Get DTR status.
+ */
+int acm_get_d0_dtr (struct usbd_function_instance *);
+
+
+/*! acm_get_bRxCarrier
+ * Get bRxCarrier (DCD) status
+ */
+int acm_get_bRxCarrier(struct usbd_function_instance *function_instance);
+
+/*! acm_set_bRxCarrier
+ * set bRxCarrier (DCD) status
+ */
+void acm_set_bRxCarrier (struct usbd_function_instance *, int);
+
+/*! acm_get_bTxCarrier
+ * Get bTxCarrier (DSR) status
+ */
+int acm_get_bTxCarrier (struct usbd_function_instance *);
+
+/*! acm_set_bTxCarrier
+ * Set bTxCarrier (DSR) status
+ */
+void acm_set_bTxCarrier(struct usbd_function_instance *function_instance, int value);
+
+/*! acm_bRingSignal
+ * Indicate Ring signal to host.
+ */
+void acm_bRingSignal (struct usbd_function_instance *);
+
+/*! acm_bBreak
+ * Indicate Break signal to host.
+ */
+void acm_bBreak (struct usbd_function_instance *);
+
+/*! acm_bOverrun
+ * Indicate Overrun signal to host.
+ */
+void acm_bOverrun (struct usbd_function_instance *);
+
+/*! acm_set_throttle
+ * Set LOOPBACK status
+ */
+void acm_set_throttle (struct usbd_function_instance *, int value);
+
+/*! acm_get_throttled
+ * Set LOOPBACK status
+ */
+int acm_get_throttled(struct usbd_function_instance *function_instance);
+
+
+/*! acm_set_local
+ * Set LOCAL status
+ */
+void acm_set_local (struct usbd_function_instance *, int value);
+
+/*! set_loopback - set loopback mode
+ * Sets LOOP flag, data received from the host will be immediately
+ * returned without passing to the upper layer.
+ */
+void acm_set_loopback (struct usbd_function_instance *, int value);
+
+
+
+/*! acm_start_recv_urbs
+ */
+void acm_restart_recv(struct usbd_function_instance *);
+int acm_start_recv_urbs(struct usbd_function_instance *);
+
+
+/*! acm_line_coding_urb_received - callback for sent URB
+ * @param urb
+ * @param rc
+ * @return int value
+ */
+int acm_line_coding_urb_received (struct usbd_urb *urb, int urb_rc);
+
+/*! acm_urb_sent_ep0 - called to indicate ep0 URB transmit finished
+ * @param urb
+ * @param rc
+ * @return int value
+ */
+int acm_urb_sent_ep0 (struct usbd_urb *urb, int rc);
+
+/*! acm_recv_urb
+ * @param urb
+ * @param rc
+ * @return int value
+ */
+int acm_recv_urb (struct usbd_urb *urb, int rc);
+
+/*! acm_urb_sent_int - called to indicate int URB transmit finished
+ * @param urb usb request block pointer
+ * @param result code
+ */
+int acm_urb_sent_int (struct usbd_urb *urb, int rc);
+
+/*! acm_function_enable - called by USB Device Core to enable the driver
+ * @param function_instance The function instance for this driver to use.
+ * @param tty_type
+ * @param os_ops
+ * @param mac_urbs
+ * @param max_bytes
+ * @return non-zero if error.
+ */
+int acm_function_enable (struct usbd_function_instance *function_instance, tty_type_t tty_type,
+ struct acm_os_ops *os_ops, int max_urbs, int max_bytes);
+
+/*! acm_function_disable - called by the USB Device Core to disable the driver
+ * @param function */
+void acm_function_disable (struct usbd_function_instance *function);
+
+/*! acm_set_configuration - called to indicate urb has been received
+ * @param function_instance
+ * @param configuration
+ */
+int acm_set_configuration (struct usbd_function_instance *function_instance, int configuration);
+
+/*! acm_set_interface - called to indicate urb has been received
+ * @param function
++ * @param wIndex
++ * @param altsetting
+ */
+int acm_set_interface (struct usbd_function_instance *function, int wIndex, int altsetting);
+
+/*! acm_reset - called to indicate urb has been received
+ * @param function
+ */
+int acm_reset (struct usbd_function_instance *function);
+
+/*! acm_suspended - called to indicate urb has been received
+ * @param function
+ */
+int acm_suspended (struct usbd_function_instance *function);
+
+/*! acm_resumed - called to indicate urb has been received
+ * @param function
+ */
+int acm_resumed (struct usbd_function_instance *function);
+
+
+/*! acm_device_request - called to process a request to endpoint or interface
+ * @param function
+ * @param request
+ * @return non-zero if error
+ */
+int acm_device_request (struct usbd_function_instance *function, struct usbd_device_request *request);
+
+
+/*! acm_endpoint_cleared - called by the USB Device Core when endpoint cleared
+ * @param function_instance The function instance for this driver
++ * @param bEndpointAddress Endpoint address
++ * @return none
+ */
+void acm_endpoint_cleared (struct usbd_function_instance *function_instance, int bEndpointAddress);
+
+/*! acm_send_queued - called by the USB Device Core when endpoint cleared
+ * @param function_instance The function instance for this driver
+ */
+int acm_send_queued (struct usbd_function_instance *function_instance);
+
+
+
+/*
+ * otg-trace tag.
+ */
+extern otg_tag_t acm_fd_trace_tag;
+
+#define MAX_QUEUED_BYTES 512
+#define MAX_QUEUED_URBS 32 // Max for write
diff --git a/drivers/otg/functions/acm/otg-config.h b/drivers/otg/functions/acm/otg-config.h
new file mode 100644
index 000000000000..f13f7cc0eee8
--- /dev/null
+++ b/drivers/otg/functions/acm/otg-config.h
@@ -0,0 +1,21 @@
+/*
+ * 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
+ */
+/*
+ * @(#) balden@belcarra.com|otg/functions/acm/otg-config.h|20060419204257|20815
+ *
+ */
+
+/*
+ * tristate " CDC ACM Function"
+ */
+/* #define CONFIG_OTG_ACM */
diff --git a/drivers/otg/functions/acm/tty-if.c b/drivers/otg/functions/acm/tty-if.c
new file mode 100644
index 000000000000..00e37c82776b
--- /dev/null
+++ b/drivers/otg/functions/acm/tty-if.c
@@ -0,0 +1,265 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/acm/tty-if.c
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/tty-if.c|20070911235624|35439
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @file otg/functions/acm/tty-if.c
+ * @brief ACM-TTY Descriptor Set
+ *
+ * This is the generic portion of the TTY version of the
+ * ACM driver.
+ *
+ * TTY
+ * Interface
+ * Upper +----------+
+ * Edge | tty-l26 |
+ * Implementation +----------+
+ *
+ *
+ * Function +----------+
+ * Descriptors | tty-if | <----
+ * Registration +----------+
+ *
+ *
+ * Function +----------+
+ * I/O | acm |
+ * Implementation +----------+
+ *
+ *
+ * Module +----------+
+ * Loading | acm-l26 |
+ * +----------+
+ *
+ *
+ *
+ * @ingroup ACMFunction
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-mcpc.h>
+
+
+#include <otg/otg-trace.h>
+#include "acm.h"
+
+/*
+ * CDC ACM Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+/*! Endpoint Index Lists
+ */
+static u8 tty_alt_endpoint_index[] = { BULK_IN, BULK_OUT, };
+static u8 tty_comm_endpoint_index[] = { INT_IN, };
+
+/*! @name Class Descriptors
+ */
+/*@{*/
+
+static struct usbd_class_header_function_descriptor tty_class_1 = {
+ bFunctionLength: 0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_HEADER,
+ bcdCDC: __constant_cpu_to_le16(0x0110),
+};
+
+static struct usbd_call_management_functional_descriptor tty_class_2 = {
+ bFunctionLength: 0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_CMF,
+ bmCapabilities: USB_CMFD_CALL_MANAGEMENT | USB_CMFD_DATA_CLASS,
+ bDataInterface: 1, /* offset to data interface within pipe group */
+};
+
+static struct usbd_class_union_function_descriptor tty_class_3 = {
+ bFunctionLength: 0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_UF,
+ bMasterInterface: 0x00,
+ bSlaveInterface: { 1 }, /* offset to slave interface within pipe group */
+};
+
+
+/*@}*/
+
+/*! ACMF Class Descriptor
+ * ACMF - c.f. Table 28
+ * currenty set to 0x2 - Support Set_Line_Coding etc,
+ *
+ * XXX Should we also set 0x4 - Supports Network_Notification?
+ */
+static struct usbd_abstract_control_functional_descriptor tty_class_4 = {
+ bFunctionLength: 0x04,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_ACMF,
+ bmCapabilities: USB_ACMFD_CONFIG /*| USB_ACMFD_SEND_BREAK */,
+};
+
+static struct usbd_generic_class_descriptor *tty_comm_class_descriptors[] =
+ { (struct usbd_generic_class_descriptor *) &tty_class_1,
+ (struct usbd_generic_class_descriptor *) &tty_class_2,
+ (struct usbd_generic_class_descriptor *) &tty_class_3,
+ (struct usbd_generic_class_descriptor *) &tty_class_4, };
+
+
+/*! Comm alternate descriptions
+ */
+static struct usbd_alternate_description tty_comm_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra CDC/ACM",
+ .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS,
+ .bInterfaceSubClass = COMMUNICATIONS_ACM_SUBCLASS,
+ .bInterfaceProtocol = 0x01,
+ .classes = sizeof (tty_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = tty_comm_class_descriptors,
+ .endpoints = sizeof (tty_comm_endpoint_index) / sizeof(u8),
+ .endpoint_index = tty_comm_endpoint_index,
+ }, };
+
+/*! Data alternate descriptions
+ */
+static struct usbd_alternate_description tty_data_alternate_descriptions[] = {
+ {
+ .iInterface = "CDC/ACM Data",
+ .bInterfaceClass = DATA_INTERFACE_CLASS,
+ .bInterfaceSubClass = COMMUNICATIONS_NO_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .endpoints = sizeof (tty_alt_endpoint_index) / sizeof(u8),
+ .endpoint_index = tty_alt_endpoint_index,
+ }, };
+
+
+/*! Interface Descriptions List */
+static struct usbd_interface_description tty_interfaces[] = {
+ {
+ .alternates = sizeof (tty_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = tty_comm_alternate_descriptions,
+ },
+ {
+ .alternates = sizeof (tty_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = tty_data_alternate_descriptions,
+ },
+};
+
+/*! Endpoint Request List
+ * XXX this should be in acm.c
+ */
+static struct usbd_endpoint_request tty_endpoint_requests[ENDPOINTS+1] = {
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 16, 4, },
+ { BULK_IN, 1, 1, 0, USB_DIR_IN | USB_ENDPOINT_BULK, 512 , 512, 0, },
+ { BULK_OUT, 1, 1, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, 512, 512, 0, },
+ { 0, },
+};
+
+
+static struct usbd_function_operations tty_function_ops;
+
+/*! function_driver
+ */
+struct usbd_interface_driver tty_function_driver = {
+ .driver = {
+ .name = "tty-if",
+ .fops = &tty_function_ops, },
+// .driver.name = "tty-if",
+// .driver.fops = &tty_function_ops,
+// .driver.function_type = function_interface,
+ .interfaces = sizeof (tty_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = tty_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = tty_endpoint_requests,
+
+ .bFunctionClass = DATA_INTERFACE_CLASS,
+ .bFunctionSubClass = COMMUNICATIONS_NO_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .iFunction = "Belcarra CDC/ACM",
+};
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+extern struct acm_os_ops tty_l26_os_ops;
+
+/*!
+ * @brief tty_if_function_enable - called by USB Device Core to enable the driver
+ * @param function_instance The function instance for this driver to use.
+ * @return non-zero if error.
+ */
+static int tty_if_function_enable (struct usbd_function_instance *function_instance)
+{
+ TRACE_MSG0(TTY, "ENABLED");
+ return acm_function_enable(function_instance, tty_if, &tty_l26_os_ops, MAX_QUEUED_URBS, MAX_QUEUED_BYTES);
+}
+
+/*! void tty_if_exit()
+ * @brief deregister tty_if interface function driver
+ * @return none
+ */
+void tty_if_exit(void)
+{
+ TRACE_MSG0(TTY, "EXIT");
+ //acm_wait_task(acm->recv_tqueue);
+ usbd_deregister_interface_function (&tty_function_driver);
+}
+
+/*! tty_function_ops
+ */
+static struct usbd_function_operations tty_function_ops = {
+ .function_enable = tty_if_function_enable,
+ .function_disable = acm_function_disable,
+
+ .device_request = acm_device_request,
+ .endpoint_cleared = acm_endpoint_cleared,
+ .set_configuration = acm_set_configuration,
+ .set_interface = acm_set_interface,
+ .reset = acm_reset,
+ .suspended = acm_suspended,
+ .resumed = acm_resumed,
+};
+
+
+/*!int tty_if_init(char*)
+ * @brief initialize and register tty_if interface driver
+ * @param usbd_module_info
+ * @return non-zero if error
+ */
+int tty_if_init(char *usbd_module_info)
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG0(TTY, "INIT");
+ return usbd_register_interface_function (&tty_function_driver, "tty-if", NULL);
+}
diff --git a/drivers/otg/functions/acm/tty-l26-os.c b/drivers/otg/functions/acm/tty-l26-os.c
new file mode 100644
index 000000000000..737ec9d921f2
--- /dev/null
+++ b/drivers/otg/functions/acm/tty-l26-os.c
@@ -0,0 +1,2017 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/acm/tty-l24-os.c
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/functions/acm/tty-l26-os.c|20070921192720|19575
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+
+
+/*!
+ * @file otg/functions/acm/tty-l26-os.c
+ * @brief ACM Function Driver private defines
+ *
+ * This is the OS portion of the TTY version of the
+ * ACM driver.
+ *
+ * TTY
+ * Interface
+ * Upper +----------+
+ * Edge | tty-l26 | <--------
+ * Implementation +----------+
+ *
+ *
+ * Function +----------+
+ * Descriptors | tty-if |
+ * Registration +----------+
+ *
+ *
+ * Function +----------+
+ * I/O | acm |
+ * Implementation +----------+
+ *
+ *
+ * Module +----------+
+ * Loading | acm-l26 |
+ * +----------+
+ *
+ *
+ *
+ * @ingroup ACMFunction
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/otg-compat.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/serial.h>
+
+#if defined(CONFIG_DEVFS_FS)
+#include <linux/devfs_fs_kernel.h>
+#endif /* defined(CONFIG_DEVFS_FS) */
+
+/* XXX
+ * Set this to first kernel version that does not have flip bufs
+ */
+#define LINUX_KERNEL_VERSION_NO_FLIP_BUF KERNEL_VERSION(2,6,17)
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include <linux/capability.h>
+#include <otg/otg-trace.h>
+#include "acm.h"
+#include "tty.h"
+
+/* ACM ***************************************************************************************** */
+
+#define ACM_TTY_MAJOR 166
+#define ACM_TTY_MINOR 0
+#define ACM_TTY_MINORS 4
+
+/*
+ * All functions take one of struct tty or struct usbd_function_instance to
+ * identify the port and/or function.
+ *
+ * Each of the following tables provides the linkage of the various
+ * components for each minor device entry:
+ *
+ * tty_table - the tty struct, provide by tty layer above us, managed by tty_l26_open/tty_l26_close
+ * function_table - the acm interface instance managed by tty_fd_enable/tty_fd_disable
+ *
+ * The tty struct driver_data field points at the private data structure for
+ * the os layer and tty layer:
+ *
+ * os_private
+ *
+ * The function instance privdata filed points at the private data structure
+ * for the function instance:
+ *
+ * acm_private
+ *
+ *
+ *
+ * N.B.
+ *
+ * 1. There are several combinations
+ *
+ * enabled reset configured host closed host opened
+ *
+ * local closed
+ *
+ * local opened
+ *
+ *
+ */
+
+static struct tty_struct **tty_table;
+static struct usbd_function_instance **function_table;
+static u8 tty_minor_count;
+
+#if defined(LINUX26)
+
+//static struct tty_struct **tty_table;
+//static struct usbd_function_instance **function_table;
+//static u8 tty_minor_count;
+#define TTYINDEX(tty) tty->index
+
+#else /* defined(LINUX26) */
+
+//static struct tty_struct ** tty_table ;
+//static struct usbd_function_instance ** function_table;
+
+
+#define TTYINDEX(tty) minor(tty->device)
+
+#endif /* defined(LINUX26) */
+
+
+/* ******************************************************************************************* */
+
+void * tty_l26_wakeup_writers(void *data);
+void tty_l26_recv_flip(void *data);
+void * tty_l26_call_hangup(void *data);
+int tty_l26_block_until_ready(struct tty_struct *tty, struct file *filp);
+int tty_l26_loop_xmit_chars(struct usbd_function_instance *function_instance, minor_chan_t chan,
+ int count, int from_user, const unsigned char *buf);
+void tty_l26_overflow_send(struct usbd_function_instance *function_instance, minor_chan_t, BOOL force);
+int tty_l26_os_recv_space_available(struct usbd_function_instance *function_instance, minor_chan_t);
+int tty_l26_os_recv_chars(struct usbd_function_instance *function_instance, u8 *cp, int n);
+unsigned int tty_l26_tiocm(struct usbd_function_instance *function_instance, minor_chan_t);
+int tty_l26_overflow_xmit_chars(struct usbd_function_instance *function_instance, minor_chan_t chan,
+ int count, int from_user, const unsigned char *buf);
+
+void * tty_l26_recv_start(void *data);
+void tty_l26_recv_start_bh(struct usbd_function_instance *function_instance);
+void tty_l26_os_line_coding(struct usbd_function_instance *function_instance);
+
+/* ******************************************************************************************* */
+/* ******************************************************************************************* */
+/* Linux TTY Functions
+ */
+/*!
+ * @brief tty_l26_open
+ *
+ * Called by TTY layer to open device.
+ *
+ * @param tty
+ * @param filp
+ * @returns non-zero for error
+ */
+STATIC int tty_l26_open(struct tty_struct *tty, struct file *filp)
+{
+
+ int index = TTYINDEX(tty);
+ minor_chan_t chan = CHAN(index);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ int used;
+ BOOL nonblocking;
+ int rc = 0;
+
+ TRACE_MSG1(TTY,"acm: %x", acm);
+
+//static struct tty_struct *tty_table;
+
+ tty_table[index] = tty;
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+ }
+
+ otg_pthread_mutex_lock(&acm->mutex);
+ UNLESS(tty->driver_data) {
+ THROW_UNLESS((os_private = CKMALLOC(sizeof(struct os_private))), error);
+ os_private->index = index;
+ tty->driver_data = os_private;
+ init_waitqueue_head(&os_private->open_wait);
+ init_waitqueue_head(&os_private->tiocm_wait);
+ init_waitqueue_head(&os_private->send_wait);
+
+ os_private->wakeup_workitem = otg_workitem_init("wakeup", tty_l26_wakeup_writers, tty, TTY);
+ os_private->hangup_workitem = otg_workitem_init("hangup", tty_l26_call_hangup, tty, TTY);
+ os_private->recv_workitem = otg_workitem_init("receive", tty_l26_recv_start, tty, TTY);
+ }
+ otg_pthread_mutex_unlock(&acm->mutex);
+
+
+ nonblocking = BOOLEAN(filp->f_flags & O_NONBLOCK); /* O_NONBLOCK is same as O_NDELAY */
+
+ TRACE_MSG2(TTY,"f_flags: %08x NONBLOCK: %x", filp->f_flags, nonblocking);
+
+ /* Lock and increment used counter, save current value.
+ * Check for exclusive open at the same time.
+ */
+ otg_pthread_mutex_lock(&acm->mutex);
+
+ /* The value of os_private->used controls MOD_{INC/DEC}_USE_COUNT, so
+ * it has to be incremented unconditionally, and no early return
+ * made until after USE_COUNT has been adjusted to match.
+ */
+ used = atomic_post_inc(&os_private->used);
+#ifdef MCEL
+ filp->f_flags |= O_EXCL; // QQQ Can we persuade MCEL to add this to their app?
+ if (nonblocking && acm && !(acm->connected)) {
+ // QQQ Is MCEL actually using this "feature"? (See below for printk)
+ rc = -EINVAL;
+ }
+ else
+#endif
+#if 0
+ if (filp->f_flags & O_EXCL) {
+ /* This is intended to be an exclusive open, so
+ * make sure no one has the device open already, and
+ * set the exclusive flag so no one can open it later.
+ */
+ if (used > 0) {
+ // Someone already has it.
+ rc = -EBUSY;
+ }
+ else {
+ os_private->flags |= TTYFD_EXCLUSIVE;
+ set_bit(TTY_EXCLUSIVE, &tty->flags);
+ }
+ }
+ if ((os_private->flags & TTYFD_EXCLUSIVE) && !capable(CAP_SYS_TTY_CONFIG))
+ {
+ // Only the superuser can do a normal open of an O_EXCL tty
+ rc = -EBUSY;
+ }
+#endif
+ otg_pthread_mutex_unlock(&acm->mutex);
+
+ // OK, now it's safe to make an early return if we are failing.
+ if (rc) {
+#ifdef MCEL
+ // This can dissappear when the "feature" above does.
+ if (-EINVAL == rc)
+ //printk(KERN_INFO "\nusb cable not connected!\n");
+#endif
+ return rc;
+ }
+
+
+ /* To truly emulate the old dual-device approach of having a non-blocking
+ * device (e.g cu0) and a blocking device (e.g. tty0) we would need to
+ * track blocking and non-blocking opens separately. We don't. This
+ * may lead to funny behavior in the multiple open case.
+ */
+ UNLESS (used) {
+
+ // First open.
+ TRACE_MSG2(TTY, "FIRST OPEN nonblocking: %x exclusive: %x", nonblocking, os_private->flags & TTYFD_EXCLUSIVE);
+ tty->low_latency = 1;
+ if (function_instance) {
+ acm_open(function_instance, chan);
+ acm_set_local(function_instance, 1);
+ }
+ os_private->flags |= TTYFD_OPENED;
+ }
+
+
+ /* All done if configured
+ */
+ //RETURN_ZERO_UNLESS(acm_ready(function_instance));
+
+ /* All done if non blocking open
+ */
+ RETURN_ZERO_IF(nonblocking);
+
+ /* Block open - wait until ready
+ */
+ TRACE_MSG0(TTY, "BLOCKING - wait until ready");
+ return tty_l26_block_until_ready(tty, filp);
+
+ /* The tty layer calls tty_l26_close() even if this open fails,
+ * so any cleanup (rc != 0) will be done there.
+ */
+ //TRACE_MSG3(acm->trace_tag,"used: %d MOD_IN_USE: %d configured: %d",
+ // atomic_read(&os_private->used), MOD_IN_USE, acm->flags & ACM_CONFIGURED);
+
+
+ CATCH(error) {
+ return -ENOMEM;
+ }
+
+}
+
+/*!
+ * @brief tty_l26_close
+ *
+ * Called by TTY layer to close device.
+ *
+ * @param tty
+ * @param filp
+ * @returns non-zero for error
+ */
+STATIC void tty_l26_close(struct tty_struct *tty, struct file *filp)
+{
+ int index = TTYINDEX(tty);
+ minor_chan_t chan = CHAN(index);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ int used;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+ TRACE_MSG0(TTY, "entered");
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+ }
+
+
+ // lock and decrement used counter, save result
+ used = atomic_pre_dec(&os_private->used);
+
+ // finished unless this is the last close
+
+ RETURN_IF (used > 0);
+
+ TRACE_MSG0(TTY,"FINAL CLOSE");
+
+ /* reset so no further operations can be started.
+ */
+ //tty->driver_data = NULL;
+
+ if (function_instance) {
+
+ /* send any overflow data if neccessary
+ */
+ tty_l26_overflow_send(function_instance, chan, TRUE);
+
+ /* tell acm layer to close
+ */
+ acm_close(function_instance, chan);
+ }
+
+ //tty->driver_data = function_instance[index];
+
+ /* This should never happen if this is the last close,
+ * but it can't hurt to check.
+ */
+ //if (os_private->open_wait_count)
+ wake_up_interruptible(&os_private->open_wait);
+
+ if (os_private->flags & TTYFD_EXCLUSIVE) {
+ os_private->flags &= ~TTYFD_EXCLUSIVE;
+ if (tty)
+ clear_bit(TTYFD_EXCLUSIVE, &tty->flags);
+ }
+
+ // XXX wait
+
+ otg_workitem_exit(os_private->wakeup_workitem);
+ otg_workitem_exit(os_private->hangup_workitem);
+ otg_workitem_exit(os_private->recv_workitem);
+
+ if (os_private) LKFREE(os_private);
+ // XXX moved up tty->driver_data = NULL;
+ tty->driver_data = NULL;
+
+ TRACE_MSG0(TTY,"FINISHED");
+
+}
+
+/* ******************************************************************************************* */
+
+
+/*!
+ * @brief tty_l26_block_until_ready
+ *
+ * Called from tty_l26_open to implement blocking open. Wait for DTR indication
+ * from acm-fd.
+ *
+ * Called by TTY layer to open device.
+ *
+ * @param tty
+ * @param filp
+ * @returns non-zero for error
+ */
+int tty_l26_block_until_ready(struct tty_struct *tty, struct file *filp)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+
+ int rc = 0;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ for (;;) {
+ /* check if the file has been closed */
+ if (tty_hung_up_p(filp)) {
+ TRACE_MSG0(TTY,"tty_hung_up_p()");
+ return -ERESTARTSYS;
+ }
+ /* check for pending signals */
+ if (signal_pending(current)) {
+ TRACE_MSG0(TTY,"signal_pending()");
+ return -ERESTARTSYS;
+ }
+ /* check if the module is unloading */
+ if (os_private->exiting) {
+ TRACE_MSG0(TTY,"module exiting()");
+ return -ENODEV;
+ }
+ /* check for what we want, do we have DCD (RTS from host POV)?
+ */
+ if (acm_get_d1_rts(function_instance)) {
+ // OK, there's somebody on the other end, let's go...
+ TRACE_MSG0(TTY,"found CAR");
+ return 0;
+ }
+ /* sleep */
+ interruptible_sleep_on(&os_private->open_wait);
+ }
+ /* NOT REACHED */
+}
+
+/* Transmit Function - called by tty layer ****************************************************** */
+
+/*!
+ * @brief tty_l26_chars_in_buffer
+ *
+ * Called by TTY layer to determine amount of pending write data.
+ *
+ * @param tty - pointer to tty data structure
+ * @return amount of pending write data
+ */
+STATIC int tty_l26_chars_in_buffer(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ minor_chan_t chan = CHAN(index);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ int rc;
+
+ TRACE_MSG1(TTY,"tty: %x", (int)tty);
+
+ rc = acm_chars_in_buffer(function_instance, chan) + atomic_read(&os_private->tty_overflow_used);
+
+ TRACE_MSG1(TTY, "chars in buffer: %d", rc);
+
+ return rc;
+}
+
+#if !defined(LINUX26)
+/*! tty_l24_write
+ *
+ * Called by TTY layer to write data.
+ * @param tty - pointer to tty data structure
+ * @param from_user - true if from user memory space
+ * @param buf - pointer to data to send
+ * @param count - number of bytes want to send.
+ * @return count - number of bytes actually sent.
+ */
+int tty_l24_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ minor_chan_t chan = CHAN(index);
+
+ TRACE_MSG4(TTY,"tty: %x -> count: %d loopback: %d from_usr: %d", tty, count, os_private->tiocm & TIOCM_LOOP, from_user);
+
+ RETURN_ZERO_UNLESS(acm);
+
+ /* loopback mode
+ */
+ if (os_private->tiocm & TIOCM_LOOP)
+ return tty_l26_loop_xmit_chars(function_instance, chan, count, from_user, buf);
+
+ /* overflow mode
+ */
+ if (atomic_read(&os_private->tty_overflow_used) || (tty_l26_chars_in_buffer(tty) && (count < 64)))
+ return tty_l26_overflow_xmit_chars(function_instance, chan, count, from_user, buf);
+
+ /* straight through mode
+ */
+ return acm_xmit_chars(function_instance, chan, count, from_user, buf);
+}
+#endif /* !defined(LINUX26) */
+
+#if defined(LINUX26)
+/*! tty_l26_write
+ *
+ * Called by TTY layer to write data.
+ * @param tty - pointer to tty data structure
+ * @param from_user - true if from user memory space
+ * @param buf - pointer to data to send
+ * @param count - number of bytes want to send.
+ * @return count - number of bytes actually sent.
+ */
+int tty_l26_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ minor_chan_t chan = CHAN(index);
+ int total_sent_chars= 0;
+ int sent_chars;
+
+
+ TRACE_MSG3(TTY,"sos909 tty: %x -> count: %d loopback: %d from_usr: %d", tty, count, os_private->tiocm & TIOCM_LOOP);
+
+ #if 0
+ {
+ int i;
+ const char *cp = buf;
+ for (i = 0; i < count; i += 8) {
+ TRACE_MSG8(TTY, " SEND [%02x %02x %02x %02x %02x %02x %02x %02x]",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+
+ }
+
+ }
+ #else
+
+ //if (count>0) printk(KERN_INFO "808 tty =>USB: Num:%d\n",count);
+ #endif
+ RETURN_ZERO_UNLESS(acm);
+
+ /* loopback mode
+ */
+ if (os_private->tiocm & TIOCM_LOOP)
+ return tty_l26_loop_xmit_chars(function_instance, chan, count, 0, buf);
+
+ /* overflow mode
+ */
+ if (atomic_read(&os_private->tty_overflow_used) || (tty_l26_chars_in_buffer(tty) && (count < 64)))
+ return tty_l26_overflow_xmit_chars(function_instance, chan, count, 0, buf);
+
+ /* straight through mode
+ */
+
+ while ((sent_chars = acm_xmit_chars(function_instance, chan, count - total_sent_chars, 0, buf + total_sent_chars))) {
+ total_sent_chars += sent_chars;
+ }
+ return total_sent_chars;
+}
+
+#endif /* defined(LINUX26) */
+
+/*! tty_l26_write_room
+ *
+ * Called by TTY layer get amount of write room available.
+ *
+ * @param tty - pointer to tty data structure
+ * @return amount of write room available
+ */
+STATIC int tty_l26_write_room(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ minor_chan_t chan = CHAN(index);
+ int rc;
+
+ TRACE_MSG1(TTY,"tty: %x", (int)tty);
+ /* loopback mode
+ */
+ if (os_private->tiocm & TIOCM_LOOP)
+ return tty_l26_os_recv_space_available(function_instance, chan);
+
+ RETURN_ZERO_UNLESS(acm);
+#if 0
+ rc = acm_write_room(function_instance, chan) +
+ (MIN(TTY_OVERFLOW_SIZE, acm->writesize) - os_private->tty_overflow_used, chan);
+
+ TRACE_MSG1(TTY, "write room: %d", rc);
+
+ return rc;
+#else
+
+ rc = acm_write_room(function_instance, chan);
+ if(rc) rc = (MIN(TTY_OVERFLOW_SIZE, acm->writesize) - atomic_read(&os_private->tty_overflow_used));
+
+ TRACE_MSG1(TTY, "write room: %d", rc);
+
+ return rc;
+#endif
+
+
+}
+
+static int throttle_count = 0;
+static int unthrottle_count = 0;
+
+/*! tty_l26_throttle
+ *
+ * Called by TTY layer to throttle (do not allow received data.)
+ *
+ * @return amount of write room available
+ */
+STATIC void tty_l26_throttle(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ minor_chan_t chan = CHAN(index);
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG1(TTY,"tty: %x", (int)tty);
+ os_private->flags |= TTYFD_THROTTLED;
+ acm_throttle(function_instance, chan);
+
+}
+
+/*! tty_l26_unthrottle
+ *
+ * Called by TTY layer to unthrottle (allow received data.)
+ *
+ * @return amount of write room available
+ */
+STATIC void tty_l26_unthrottle(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ minor_chan_t chan = CHAN(index);
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG1(TTY,"tty: %x", (int)tty);
+ os_private->flags &= ~TTYFD_THROTTLED;
+ acm_unthrottle(function_instance, chan);
+
+ /* possible recv was waiting
+ */
+ //acm_restart_recv(function_instance);
+ //tty_l26_recv_start_bh(function_instance);
+
+
+ /* This function is called while the TTY_DONT_FLIP flag is still
+ * set, so there is no point trying to push the flip buffer. Just
+ * try to queue some recv urbs, and keep trying until we do manage
+ * to get some queued.
+ */
+ //tty_schedule_flip(tty);
+
+}
+
+
+unsigned int tty_l26_tiocm(struct usbd_function_instance *function_instance, minor_chan_t chan);
+
+static int tty_l26_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
+/*! tty_l26_fd_ioctl
+ *
+ * Used by TTY layer to execute IOCTL command.
+ *
+ * Called by TTY layer to open device.
+ *
+ * Unhandled commands must return -ENOIOCTLCMD for correct operation.
+ *
+ * @param tty
+ * @param file
+ * @param cmd
+ * @param arg
+ * @returns non-zero for error
+ */
+STATIC int tty_l26_fd_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int index = TTYINDEX(tty);
+ minor_chan_t chan = CHAN(index);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+
+ unsigned int mask;
+ unsigned int newctrl;
+ int rc;
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //TRACE_MSG3(TTY,"tty: %x cmd: %08x arg: %08x", (int)tty, cmd, arg);
+ TRACE_MSG3(TTY,"entered: %8x dir: %x size: %x", cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd));
+
+ switch(chan) {
+
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+
+ }
+
+ switch(cmd) {
+#if !defined(LINUX26)
+ case TIOCMGET:
+ //TRACE_MSG1(TTY, "TIOCMGET: tiocm: %08x", tiocm);
+ os_private->tiocm = tty_l26_tiocm(function_instance, chan);
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &os_private->tiocm, sizeof(int)));
+ return 0;
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET: {
+ u32 tiocm = 0, set = 0, clear = 0;
+
+ TRACE_MSG0(TTY, "TIOCMXXX: copying tiocm arguement");
+
+ TRACE_MSG1(TTY, "access: %d", access_ok(VERIFY_READ, (void *)arg, sizeof(int)));
+ RETURN_EINVAL_UNLESS(access_ok(VERIFY_READ, (void *)arg, sizeof(int)));
+
+
+ //RETURN_EINVAL_IF (copy_from_user(&tiocm, (void *)arg, sizeof(int)));
+ rc = copy_from_user((void *)&tiocm, (void *)arg, sizeof(int));
+ TRACE_MSG2(TTY, "copy_from_user: %d tiocm: %08x", rc, tiocm);
+
+ switch (cmd) {
+ case TIOCMBIS:
+ set = tiocm;
+ break;
+ case TIOCMBIC:
+ clear = tiocm;
+ break;
+ case TIOCMSET:
+ set = tiocm;
+ clear = ~set;
+ break;
+ }
+
+ return tty_l26_tiocmset(tty, NULL, set, clear);
+ }
+
+#endif /* !defined(LINUX26) */
+
+ case TIOCGSERIAL:
+ TRACE_MSG0(TTY, "TIOCGSERIAL");
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &os_private->serial_struct, sizeof(struct serial_struct)));
+ return 0;
+
+ case TIOCSSERIAL:
+ TRACE_MSG0(TTY, "TIOCSSERIAL");
+ //RETURN_EFAULT_IF (copy_from_user(&os_private->serial_struct, (void *)arg, sizeof(struct serial_struct)));
+ return -EINVAL;
+
+ case TIOCSERCONFIG:
+ case TIOCSERGETLSR: /* Get line status register */
+ case TIOCSERGSTRUCT:
+ TRACE_MSG0(TTY, "TIOCSER*");
+ return -EINVAL;
+
+ /*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ *
+ * Note: CTS is always true, RI is always false.
+ * DCD and DSR always follow DTR signal from host.
+ */
+ case TIOCMIWAIT:
+ TRACE_MSG0(TTY, "TIOCGMIWAIT");
+ while (1) {
+
+ u32 saved_tiocm = os_private->tiocm;
+ u32 changed_tiocm;
+
+ interruptible_sleep_on(&os_private->tiocm_wait);
+
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ os_private->tiocm = tty_l26_tiocm(function_instance, chan);
+ changed_tiocm = saved_tiocm ^ os_private->tiocm;
+ RETURN_ZERO_IF ( (changed_tiocm | TIOCM_CAR) | (changed_tiocm | TIOCM_DSR) );
+ return -EIO;
+ /* loop */
+ }
+ /* NOTREACHED */
+
+ /*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ * RI where only 0->1 is counted.
+ */
+ case TIOCGICOUNT:
+ TRACE_MSG0(TTY, "TIOCGICOUNT");
+ if (copy_to_user((void *)arg, &os_private->tiocgicount, sizeof(int)))
+ return -EFAULT;
+ return 0;
+
+ case TCSETS:
+ TRACE_MSG0(TTY, "TCSETS: ");
+ return -ENOIOCTLCMD;
+ case TCFLSH:
+ TRACE_MSG0(TTY, "TCFLSH: x");
+ return -ENOIOCTLCMD;
+
+ case TCGETS:
+ TRACE_MSG0(TTY, "TCGETS: ");
+ return -ENOIOCTLCMD;
+
+ default:
+ TRACE_MSG1(TTY, "unknown cmd: %08x", cmd);
+ return -ENOIOCTLCMD;
+ }
+ return -ENOIOCTLCMD;
+
+
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,20)
+#define _TERMIOS_ ktermios
+#else
+#define _TERMIOS_ termios
+#endif
+
+/*! tty_l26_set_termios
+ *
+ * Used by TTY layer to set termios structure according to current status.
+ *
+ * @param tty - pointer to acm private data structure
+ * @param termios_old - termios structure
+ */
+STATIC void tty_l26_set_termios(struct tty_struct *tty, struct _TERMIOS_ *termios_old)
+{
+ int index = TTYINDEX(tty);
+ minor_chan_t chan = CHAN(index);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+
+ unsigned int c_cflag = tty->termios->c_cflag;
+
+ TRACE_MSG1(TTY,"tty: %x", (int)tty);
+
+ switch(chan) {
+ case data_chan:
+ break;
+ case command_chan:
+ default:
+ break;
+
+ }
+
+ /* see if CLOCAL has changed */
+ if ((termios_old->c_cflag ^ tty->termios->c_cflag ) & CLOCAL)
+ acm_set_local(function_instance, tty->termios->c_cflag & CLOCAL);
+
+ /* save cflags */
+ //os_private->c_cflag = c_cflag;
+
+ /* send break? */
+ if ((termios_old->c_cflag & CBAUD) && !(c_cflag & CBAUD))
+ acm_bBreak(function_instance);
+}
+
+
+/*! tty_l26_wait_until_sent
+ *
+ * Used by TTY layer to wait until pending data drains (is sent.)
+ *
+ * @param tty - pointer to acm private data structure
+ * @param timeout - wait this amount of time, or forever if zero
+ */
+static void tty_l26_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ minor_chan_t chan = CHAN(index);
+
+ TRACE_MSG0(TTY, "--");
+
+ RETURN_UNLESS(acm_send_queued(function_instance));
+
+ /* XXX This will require a wait structure and flag */
+ interruptible_sleep_on(&os_private->send_wait);
+}
+
+/*! tty_l26_flush_chars
+ *
+ * Used by TTY layer to flush pending data to hardware.
+ *
+ * @param tty - pointer to acm private data structure
+ */
+static void tty_l26_flush_chars(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ minor_chan_t chan = CHAN(index);
+
+ TRACE_MSG0(TTY, "--");
+
+ /* queue data in overflow buffer */
+ tty_l26_overflow_send(function_instance, chan, TRUE);
+}
+
+/*! tty_l26_flush_buffer
+ *
+ * Used by TTY layer to flush pending data to /dev/null (cancel.)
+ *
+ * @param tty - pointer to acm private data structure
+ */
+static void tty_l26_flush_buffer(struct tty_struct *tty)
+{
+ int index = TTYINDEX(tty);
+ struct os_private *os_private = tty->driver_data;
+ struct usbd_function_instance *function_instance = function_table[index];
+ minor_chan_t chan = CHAN(index);
+
+ TRACE_MSG0(TTY, "--");
+
+ /* clear overflow buffer */
+
+ atomic_set(&os_private->tty_overflow_used, 0);
+}
+
+
+#if defined(LINUX26)
+/*! tty_operations
+ */
+static struct tty_operations tty_ops = {
+
+ .open = tty_l26_open,
+ .close = tty_l26_close,
+ .write = tty_l26_write,
+ .write_room = tty_l26_write_room,
+ .ioctl = tty_l26_fd_ioctl,
+ .throttle = tty_l26_throttle,
+ .unthrottle = tty_l26_unthrottle,
+ .chars_in_buffer = tty_l26_chars_in_buffer,
+ .set_termios = tty_l26_set_termios,
+ #if 0
+ .wait_until_sent = tty_l26_wait_until_sent,
+ .flush_chars = tty_l26_flush_chars,
+ .flush_buffer = tty_l26_flush_buffer,
+ #endif
+};
+
+/*! tty_driver
+ */
+static struct tty_driver *tty_driver;
+
+#else /* defined(LINUX26) */
+
+static int tty_refcount;
+static struct tty_struct *tty_table1[ACM_TTY_MINORS];
+static struct termios *tty_termios[ACM_TTY_MINORS];
+static struct termios *tty_termios_locked[ACM_TTY_MINORS];
+
+static struct tty_driver tty_driver = {
+
+
+ .open = tty_l26_open,
+ .close = tty_l26_close,
+ .write = tty_l24_write,
+ .write_room = tty_l26_write_room,
+ .ioctl = tty_l26_fd_ioctl,
+ .throttle = tty_l26_throttle,
+ .unthrottle = tty_l26_unthrottle,
+ .chars_in_buffer = tty_l26_chars_in_buffer,
+ .set_termios = tty_l26_set_termios,
+
+ .refcount = &tty_refcount,
+ .table = tty_table1,
+ .termios = tty_termios,
+ .termios_locked = tty_termios_locked,
+};
+
+#endif /* defined(LINUX26) */
+
+/* Transmit Function - called by tty layer ****************************************************** */
+
+/* ******************************************************************************************* */
+/* ******************************************************************************************* */
+/* TTY OS Functions
+ */
+
+#define LOOP_BUF 16
+/*!
+ * @brief tty_l26_loop_xmit_chars
+ *
+ * Implement data loop back, send xmit data back as received data.
+ *
+ * @param function_instance
+ * @param chan
+ * @param count - number of bytes to send
+ * @param from_user - true if from user memory space
+ * @param buf - pointer to data to send
+ * @return count - number of bytes actually sent.
+ */
+int tty_l26_loop_xmit_chars(struct usbd_function_instance *function_instance, minor_chan_t chan,
+ int count, int from_user, const unsigned char *buf)
+{
+
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty->driver_data;
+ int received = 0;
+ int rc=0;
+
+ TRACE_MSG2(TTY,"acm[%x] %x", chan, acm);
+
+ while (count > 0) {
+ u8 copybuf[LOOP_BUF];
+ int i;
+ int space = tty_l26_os_recv_space_available(function_instance, chan);
+ int recv = MIN (space, LOOP_BUF);
+ recv = MIN(recv, count);
+
+ //TRACE_MSG7(TTY, "buf: %8x buf+received: %8x received: %d from_user: %d count: %d space: %d recv: %d\n",
+ // buf, buf + received, received, from_user, count, space, recv);
+
+ BREAK_UNLESS(recv);
+
+ if (from_user)
+ rc=copy_from_user ((void *)copybuf, (void*)(buf + received), recv);
+ else
+ memcpy ((void *)copybuf, (void*)(buf + received), recv);
+
+ count -= recv;
+ received += recv;
+ TRACE_MSG3(TTY, "count: %d recv: %d received: %d", count, recv, received);
+ tty_l26_os_recv_chars(function_instance, copybuf, recv);
+ }
+ return received;
+}
+
+/*!
+ * @brief tty_l26_overflow_send
+ *
+ * Check if there is data in overflow buffer and send if neccessary.
+ *
+ * @param function_instance - pointer to acm private data structure
+ * @param chan
+ * @param force - force the send
+ * @return none
+ */
+void tty_l26_overflow_send(struct usbd_function_instance *function_instance, minor_chan_t chan, int force)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty->driver_data;
+ int chars_in_buffer;
+ int count;
+
+ TRACE_MSG2(TTY,"acm[%x] %x", chan, acm);
+
+ otg_pthread_mutex_lock(&acm->mutex);
+
+ chars_in_buffer = acm_chars_in_buffer(function_instance, chan);
+
+ TRACE_MSG3(TTY, "overflow_used: %d chars_in_buffer: %d force: %d",
+ atomic_read(&os_private->tty_overflow_used), chars_in_buffer, force);
+
+ if (TRUE || !chars_in_buffer || (atomic_read(&os_private->tty_overflow_used) > 200)) {
+
+ count = acm_xmit_chars(function_instance, chan, atomic_read(&os_private->tty_overflow_used),
+ 0, os_private->tty_overflow_buffer);
+
+ TRACE_MSG2(TTY, "overflow_used: %d count: %d", atomic_read(&os_private->tty_overflow_used), count);
+
+ if((count==0) && (atomic_read(&os_private->tty_overflow_used)>0))
+ {
+ }else{
+ atomic_set(&os_private->tty_overflow_used, 0);
+ }
+
+ }
+ otg_pthread_mutex_unlock(&acm->mutex);
+}
+
+
+/*!
+ * @brief tty_l26_overflow_xmit_chars
+ *
+ * Implement overflow buffer. The TTY layer implements echo with single
+ * character writes which can quickly exhaust the urbs allowed for sending.
+ * This results in data being lost.
+ *
+ * This function is called to accumulate small amounts of data when there is
+ * already an bulk in urb in the queue.
+ *
+ * The urb sent function calls wakeup writers which will call
+ * tty_l26_overflow_send which will send the data.
+ *
+ * @param function_instance - pointer to acm private data structure
+ * @param chan
+ * @param count - number of bytes to send
+ * @param from_user - true if from user memory space
+ * @param buf - pointer to data to send
+ * @return count - number of bytes actually sent.
+ */
+int tty_l26_overflow_xmit_chars(struct usbd_function_instance *function_instance, minor_chan_t chan,
+ int count, int from_user, const unsigned char *buf)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty->driver_data;
+ int overflow_room;
+ int write_room = acm_write_room(function_instance, chan);
+ int sent = 0;
+ int rc=0;
+
+ TRACE_MSG4(TTY,"acm[%x] %x count: %d from_user:%d", chan, acm, count, from_user);
+
+ RETURN_ZERO_UNLESS(acm);
+
+ do {
+ otg_pthread_mutex_lock(&acm->mutex);
+
+ if ((overflow_room = MIN(TTY_OVERFLOW_SIZE, acm->writesize) - atomic_read(&os_private->tty_overflow_used)) > 0) {
+
+ int tosend = MIN(overflow_room, count);
+ if (from_user)
+ rc=copy_from_user ((void *)os_private->tty_overflow_buffer + atomic_read(&os_private->tty_overflow_used),
+ (void*)buf, tosend);
+ else
+ memcpy ((void *)os_private->tty_overflow_buffer + atomic_read(&os_private->tty_overflow_used),
+ (void*)buf, tosend);
+
+ atomic_add(tosend, &os_private->tty_overflow_used);
+ count -= tosend;
+ buf += tosend;
+ sent += tosend;
+ }
+ else
+ {
+ count = 0;
+ }
+ otg_pthread_mutex_unlock(&acm->mutex);
+
+ /* start sending from overflow if neccessary
+ */
+ tty_l26_overflow_send(function_instance, chan, count ? TRUE : FALSE);
+ } while (count);
+
+ return sent;
+}
+/*! tty_l26_setbit - set specific bit
+ * @brief set specific bit and output message
+ *
+ * @param result - new value after setbit
+ * @param mask - bit to set
+ * @param value - bool to decide whether to set
+ * @param msg - message to output
+ * @return new value after set bit
+ */
+
+static int tty_l26_setbit(u32 result, u32 mask, BOOL value, char *msg)
+{
+ UNLESS (value)
+ return result;
+
+ result |= mask;
+
+ TRACE_MSG3(TTY, "result: %04x mask: %04x %s", result, mask, msg);
+ return result;
+}
+
+/*!
+ * @brief tty_l26_tiocm
+ * @param function_instance
+ * @param chan
+ * @return - computed tiocm value
+ */
+unsigned int tty_l26_tiocm(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ u32 tiocm = 0;
+ TRACE_MSG2(TTY,"acm[%x] %x", chan, (int)function_instance ? function_instance->privdata : NULL );
+ switch(chan) {
+ case data_chan:
+ tiocm = tty_l26_setbit(tiocm, TIOCM_DTR, acm_get_bRxCarrier(function_instance), "TIOCM_DTR (bRxCarrier)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_RTS, !acm_get_throttled(function_instance), "TIOCM_RTS (TRUE)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_CTS, acm_get_d0_dtr(function_instance), "TIOCM_CTS (TRUE)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_CAR, acm_get_d1_rts(function_instance), "TIOCM_CAR (D1 RTS)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_RI, FALSE, "TIOCM_RING");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_DSR, acm_get_d0_dtr(function_instance), "TIOCM_DSR (D0 DTR)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_OUT1, FALSE, "TIOCM_OUT1 (FALSE)");
+ tiocm = tty_l26_setbit(tiocm, TIOCM_OUT2, FALSE, "TIOCM_OUT2 (FALSE)");
+ return tiocm;
+
+ case command_chan:
+ default:
+ return 0;
+ }
+}
+
+#if defined(LINUX26) || defined(LINUX24)
+
+/*! tty_l26_tiocmget
+ *
+ * Peripheral Host Internal
+ * TIOCM_DTR bRxCarrier
+ * TIOCM_RTS Always TRUE
+ * TIOCM_CTS Always TRUE
+ * TIOCM_CAR D1
+ * TIOCM_RI Always FALSE
+ * TIOCM_DSR D0
+ * TIOCM_OUT1 bRingSignal
+ * TIOCM_OUT2 bOverrrun
+ * TIOCM_LOOP Loopback
+ */
+static int tty_l26_tiocmget(struct tty_struct *tty, struct file *file)
+{
+
+ int index = TTYINDEX(tty);
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ unsigned int result = 0;
+ unsigned int msr = 0;
+ unsigned int mcr = 0;
+
+ TRACE_MSG1(TTY, "acm: %x", (int)acm);
+ RETURN_ZERO_UNLESS(acm);
+
+ return tty_l26_tiocm(function_instance, data_chan);
+
+}
+
+/*! tty_l26_tiocsget
+ * Peripheral Host Internal
+ * TIOCM_DTR bRxCarrier
+ * TIOCM_RTS Always TRUE
+ * TIOCM_OUT1 bRingSignal
+ * TIOCM_OUT2 bOverrun
+ * TIOCM_LOOP Loopback
+ *
+ */
+static int tty_l26_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ int index = TTYINDEX(tty);
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ unsigned int result = 0;
+ unsigned int msr = 0;
+ unsigned int mcr = 0;
+
+ TRACE_MSG3(TTY,"acm: %x set: %04x clear: %04x", (int)acm, set, clear);
+ RETURN_ZERO_UNLESS(acm);
+
+
+ /* Set DTR -> DTR */
+ if (set & TIOCM_DTR)
+ acm_set_bRxCarrier(function_instance, 1);
+ if (clear & TIOCM_DTR)
+ acm_set_bRxCarrier(function_instance, 0);
+
+ /* Set Loop back */
+ if (set & TIOCM_LOOP)
+ acm_set_loopback(function_instance, 1);
+ if (clear & TIOCM_LOOP)
+ acm_set_loopback(function_instance, 0);
+
+ /* OUT1->Ring */
+ if ((set & TIOCM_OUT1))
+ acm_bRingSignal(function_instance);
+
+ /* OUT2->Overrun */
+ if ((set & TIOCM_OUT2))
+ acm_bOverrun(function_instance);
+
+ /* !RTS->Break */
+ if (set & TIOCM_RTS)
+ acm_set_throttle(function_instance, 0);
+ if (clear & TIOCM_RTS)
+ acm_set_throttle(function_instance, 1);
+
+ return 0;
+}
+
+/*! tty_l26_break_ctl
+ */
+static int tty_l26_break_ctl(struct tty_struct *tty, int flag)
+{
+ int index = TTYINDEX(tty);
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ RETURN_ZERO_UNLESS(acm);
+
+ if (flag)
+ acm_bBreak(function_instance);
+ return 0;
+}
+
+#endif /* defined(LINUX26) */
+/* ********************************************************************************************** */
+/* ********************************************************************************************* */
+
+/*!
+ * @brief tty_l26_call_hangup
+ *
+ * Bottom half handler to safely send hangup signal to process group.
+ *
+ * @param data
+ * @return none
+ */
+void * tty_l26_call_hangup(void *data)
+{
+ struct tty_struct *tty = data;
+ struct os_private *os_private = tty ? tty->driver_data : NULL;
+
+ /* XXX Do we need to protect this
+ */
+ RETURN_NULL_UNLESS(tty && os_private);
+
+ TRACE_MSG3(TTY,"tty: %x c_cflag: %02x CLOCAL: %d", tty,
+ tty->termios->c_cflag,
+ BOOLEAN(tty->termios->c_cflag & CLOCAL)
+ );
+
+
+ //if (tty && !(os_private->c_cflag & CLOCAL))
+ tty_hangup(tty);
+
+ wake_up_interruptible(&os_private->open_wait);
+
+ TRACE_MSG0(TTY,"exited");
+ return NULL;
+}
+
+/*!
+ * @brief tty_l26_os_schedule_hangup
+ *
+ * Schedule hangup bottom half handler.
+ *
+ * @param function_instance
+ * @return none
+
+ */
+void tty_l26_os_schedule_hangup(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ RETURN_UNLESS(os_private);
+
+ TRACE_MSG0(TTY, "entered");
+ //tty_l26_schedule(tty, &os_private->hqueue);
+ otg_workitem_start(os_private->hangup_workitem);
+}
+
+
+/*! tty_l26_wakeup_writers
+ *
+ * Bottom half handler to wakeup pending writers.
+ *
+ * @param data - pointer to acm private data structure
+ */
+void * tty_l26_wakeup_writers(void *data)
+{
+ struct tty_struct *tty = data;
+ struct os_private *os_private = tty ? tty->driver_data : NULL;
+ int index = os_private->index;
+ minor_chan_t chan = CHAN(index);
+ struct usbd_function_instance *function_instance = function_table[index];
+
+
+ unsigned long flags;
+
+ TRACE_MSG1(TTY,"tty: %x", tty);
+
+ /* XXX Do we need to protect this
+ */
+ RETURN_NULL_UNLESS(os_private);
+
+ //TRACE_MSG2(TTY,"used: %d MOD_IN_USE: %d", atomic_read(&os_private->used), MOD_IN_USE);
+
+ RETURN_NULL_UNLESS(acm_ready(function_instance));
+ RETURN_NULL_UNLESS(atomic_read(&os_private->used));
+ RETURN_NULL_UNLESS(tty);
+
+ /* start sending from overflow buffer if necessary
+ */
+ tty_l26_overflow_send(function_instance, chan, FALSE);
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+
+ wake_up_interruptible(&tty->write_wait);
+ return NULL;
+}
+
+/*!
+ * @brief tty_l26_os_wakeup_opens
+ *
+ * Called by acm_fd to wakeup processes blocked in open waiting for DTR.
+ *
+ * @param function_instance
+ * @return none
+ */
+void tty_l26_os_wakeup_opens(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ TRACE_MSG1(TTY,"acm: %x", acm);
+
+ /* XXX Do we need to protect this
+ */
+ RETURN_UNLESS(os_private);
+
+ //if (os_private->open_wait_count > 0)
+ wake_up_interruptible(&os_private->open_wait);
+}
+
+/*!
+ * @brief tty_l26_os_schedule_wakeup_writers
+ *
+ * Called by acm-fd to schedule a wakeup writes bottom half handler.
+ *
+ * @param function_instance - pointer to acm private data structure
+ * @return none
+ */
+void tty_l26_os_schedule_wakeup_writers(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ /* XXX Do we need to protect this
+ */
+ RETURN_UNLESS(os_private);
+
+ //tty_l26_schedule(tty, &os_private->wqueue);
+ otg_workitem_start(os_private->wakeup_workitem);
+ // verify ok
+ wake_up_interruptible(&os_private->send_wait);
+}
+
+/*!
+ * @brief tty_l26_os_wakeup_state
+ *
+ * Called by acm_fd to wakeup processes blocked waiting for state change
+ *
+ * @param function_instance - pointer to acm private data structure
+ * @return none
+ */
+void tty_l26_os_wakeup_state(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ /* XXX Do we need to protect this
+ */
+ RETURN_UNLESS(os_private);
+
+ TRACE_MSG0(TTY, "entered");
+ wake_up_interruptible(&os_private->tiocm_wait);
+}
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * @brief tty_l26_os_recv_space_available
+ *
+ * Used by acm-fd to determine receive space available. This will determine
+ * how many receive urbs can be queued as there are never more receive urbs
+ * pending than there is currently room for data to be received.
+ *
+ * @param function_instance
+ * @param chan
+ * @return count - number of bytes available
+ */
+int tty_l26_os_recv_space_available(struct usbd_function_instance *function_instance, minor_chan_t chan)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : NULL;
+ int room_len = 0;
+ int rc;
+
+ //TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ RETURN_ZERO_UNLESS(tty);
+#if 1
+ //room_len=tty_buffer_request_room(tty, TTY_FLIPBUF_SIZE);
+ TRACE_MSG1(TTY,"OS space room:%d",room_len);
+ printk(KERN_INFO"%s: space room:%d, TTY_FLIPBUF_SIZE:%d\n", __FUNCTION__,room_len,TTY_FLIPBUF_SIZE);
+ return room_len;
+
+#else
+
+#if LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF
+ UNLESS (!tty->flip.count || test_bit(TTY_THROTTLED, &tty->flags) || test_bit(TTY_DONT_FLIP, &tty->flags)) {
+ TRACE_MSG2(TTY, "FLIPPING count: %d available: %d", tty->flip.count, TTY_FLIPBUF_SIZE - tty->flip.count);
+ tty_flip_buffer_push(tty);
+ }
+ TRACE_MSG7(TTY, "FLIP count: %d available: %d read_cnt: %d minimum_to_wake: %d %s %s %s",
+ tty->flip.count,
+ TTY_FLIPBUF_SIZE - tty->flip.count,
+ tty->read_cnt, tty->minimum_to_wake,
+ tty->flags & TTY_THROTTLED ? "THROTTLED" : "",
+ tty->flags & TTY_DONT_FLIP ? "DONT_FLIP" : "",
+ tty->real_raw ? "REAL RAW" : ""
+ );
+ /* not only do we have to test the local flip buffer for space, but if
+ * we are in raw mode check the n_tty buffer availability.
+ *
+ * In n_tty_receive_buf(), when in raw mode, the data is saved into
+ * a 4kb wrap around ring buffer. So we must ensure that we never
+ * attempt push more data to that layer than will fit.
+ */
+ return MIN( (tty->real_raw ? (N_TTY_BUF_SIZE - acm->readsize - tty->read_cnt) : TTY_FLIPBUF_SIZE ),
+ (TTY_FLIPBUF_SIZE - acm->readsize) - tty->flip.count);
+#else /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+ UNLESS((TTY_FLIPBUF_SIZE != tty_buffer_request_room(tty, TTY_FLIPBUF_SIZE))
+ || test_bit(TTY_THROTTLED, &tty->flags)
+ ) {
+ tty_flip_buffer_push(tty);
+ }
+ return MIN((tty->real_raw ? (N_TTY_BUF_SIZE - acm->readsize - tty->read_cnt) : TTY_FLIPBUF_SIZE),
+ (tty_buffer_request_room(tty, TTY_FLIPBUF_SIZE) - acm->readsize));
+
+#endif /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+#endif
+}
+
+
+/*!
+ * @brief tty_l26_os_recv_chars
+ *
+ * Called by acm-fd when data has been received. This will
+ * receive n bytes starting at cp. This will never be
+ * more than the last call to tty_l26_os_recv_space_available().
+ * This will be called from interrupt context.
+ *
+ * @param function_instance
+ * @param n - number of bytes to send
+ * @param cp - pointer to data to send
+ * @return non-zero if error
+ */
+int tty_l26_os_recv_chars(struct usbd_function_instance *function_instance, u8 *cp, int n)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ int buf_size;
+ RETURN_EINVAL_UNLESS(tty);
+#if 1
+ otg_pthread_mutex_lock(&acm->mutex);
+ buf_size=tty_buffer_request_room(tty,n);
+ //printk(KERN_INFO"%s: USB got data and push to tty:%d, buf_size:%d\n", __FUNCTION__,n,buf_size);
+ tty_insert_flip_string(tty, cp, buf_size);
+ tty_flip_buffer_push(tty);
+ otg_pthread_mutex_unlock(&acm->mutex);
+ return 0;
+#else
+
+#if LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF
+ if (TTY_FLIPBUF_SIZE < (tty->flip.count + n)) {
+ TRACE_MSG3(TTY, "TTY_FLIP_BUF_SIZE: %d count: %d n: %d", TTY_FLIPBUF_SIZE, tty->flip.count, n);
+ printk(KERN_INFO"%s: FLIP BUFFER OVERFLOW ERROR\n", __FUNCTION__);
+ return -EINVAL;
+ }
+#endif /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+
+#if 1
+ if (tty->real_raw) {
+ if ((N_TTY_BUF_SIZE - 1) < (tty->read_cnt + n)) {
+ TRACE_MSG3(TTY, "N_TTY_BUF_SIZE::%d, count: %d n: %d", N_TTY_BUF_SIZE, tty->read_cnt, n);
+ printk(KERN_INFO"%s: N_TTY_BUF_SIZE:%d, tty->read_cnt: %d just rcv n: %d\n", __FUNCTION__, N_TTY_BUF_SIZE, tty->read_cnt, n);
+ printk(KERN_INFO"%s: N_TTY BUFFER OVERFLOW ERROR\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ }
+#endif
+
+ otg_pthread_mutex_lock(&acm->mutex);
+
+#if LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF
+ TRACE_MSG5(TTY, "tty: %x cb: %x fb: %x count: %d buf: %d",
+ tty, tty->flip.char_buf_ptr, tty->flip.flag_buf_ptr, tty->flip.count, tty->flip.buf_num);
+#endif /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+
+
+ TRACE_MSG2(TTY, " sos909 FLIP: %d buffer: %x", n, cp);
+ #if 1
+
+ {
+
+ int i;
+
+ for (i = 0; i < n; i += 8) {
+ TRACE_MSG8(TTY, "sos909 %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+
+ }
+ #else
+
+
+ int i;
+ char prompt[1028];
+ for (i = 0; i < n; i++) sprintf((prompt+3*i),"%02X ",cp[i]);
+ //TRACE_MSG2(TTY,"808 Hardware to tty: num%d, %s\n",n,prompt);
+
+ if (n>0) printk(KERN_INFO "808 tty<=USB: Num: %d|| %s\n\n",n,prompt);
+ #endif
+
+#if LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF
+ memcpy(tty->flip.char_buf_ptr, cp, n);
+ memset(tty->flip.flag_buf_ptr, TTY_NORMAL, n);
+ tty->flip.count += n;
+ tty->flip.char_buf_ptr += n;
+ tty->flip.flag_buf_ptr += n;
+ atomic_add(n, &acm->bytes_forwarded);
+ TRACE_MSG5(TTY, "tty: %x cb: %x fb: %x count: %d buf: %d",
+ tty, tty->flip.char_buf_ptr, tty->flip.flag_buf_ptr, tty->flip.count, tty->flip.buf_num);
+
+#else /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+ n = tty_insert_flip_string(tty, cp, n);
+#endif /* LINUX_VERSION_CODE < LINUX_KERNEL_VERSION_NO_FLIP_BUF */
+ tty_flip_buffer_push(tty);
+ //otg_led(LED1, FALSE);
+ otg_pthread_mutex_unlock(&acm->mutex);
+ return 0;
+#endif
+
+}
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * @brief tty_l26_recv_start
+ *
+ * Bottom half handler to restart acm recv queue...
+ *
+ * This is implemented here because the background scheduling may b3 OS
+ * dependant.. but it must call the common acm_start_recv_urbs() function to
+ * do the work.
+ *
+ * @param data
+ * @return none
+ */
+void * tty_l26_recv_start(void *data)
+{
+ struct tty_struct *tty = data;
+ int index = TTYINDEX(tty);
+ struct usbd_function_instance *function_instance = function_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : NULL;
+
+ RETURN_NULL_UNLESS(function_instance);
+
+ TRACE_MSG3(TTY,"tty: %x c_cflag: %02x CLOCAL: %d", tty,
+ tty->termios->c_cflag,
+ BOOLEAN(tty->termios->c_cflag & CLOCAL)
+ );
+
+ /* keep trying to start urbs until we have enough or -1 (not ready)
+ */
+ while (!acm_start_recv_urbs(function_instance)) {
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ TRACE_MSG0(TTY, "SLEEP START");
+ interruptible_sleep_on_timeout(&acm->recv_wait, 1);
+ TRACE_MSG0(TTY, "SLEEP FINISH");
+ }
+
+ TRACE_MSG0(TTY,"exited");
+ return NULL;
+}
+
+/*!
+ * @brief tty_l26_recv_start_bh
+ *
+ * Schedule hangup bottom half handler.
+ *
+ * @param function_instance
+ * @return none
+
+ */
+void tty_l26_recv_start_bh(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ RETURN_UNLESS(os_private);
+
+ TRACE_MSG0(TTY, "entered");
+
+ otg_workitem_start(os_private->recv_workitem);
+}
+
+void tty_l26_os_line_coding(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ struct tty_struct *tty = tty_table[index];
+ struct os_private *os_private = tty ? tty->driver_data : tty;
+ struct cdc_acm_line_coding *line_coding = &acm->line_coding;
+
+ TRACE_MSG1(TTY,"acm: %x", (int)acm);
+
+ RETURN_UNLESS(os_private);
+
+ TRACE_MSG0(TTY, "entered");
+
+ os_private->serial_struct.custom_divisor = 1;
+ os_private->serial_struct.baud_base = line_coding->dwDTERate;
+ os_private->serial_struct.flags = line_coding->bCharFormat << 16 |
+ line_coding->bParityType << 8 | line_coding->bDataBits;
+}
+
+/* ********************************************************************************************* */
+/*!
+ * @brief tty_l26_os_enable
+ *
+ * Called by acm-fd when the function driver is enabled.
+ * @param function_instance
+ * @return int
+ */
+int tty_l26_os_enable(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index;
+
+ TRACE_MSG1(TTY,"acm: %x", acm);
+ // XXX MODULE LOCK HERE
+
+ // XXX should do something based on wIndex...
+ for (index = 0; index < acm->minors; index++) {
+ struct tty_struct *tty;
+ struct os_private *os_private;
+ CONTINUE_IF (function_table[index]);
+ function_table[index] = function_instance;
+ acm->index = index;
+ tty = tty_table[index];
+ os_private = tty ? tty->driver_data : NULL;
+
+ TRACE_MSG1(TTY, "OS Flags: %x", os_private ? os_private->flags : 0);
+
+ RETURN_ZERO_UNLESS(os_private && (os_private->flags & TTYFD_OPENED));
+ acm->flags |= ACM_OPENED;
+ tty_l26_os_schedule_wakeup_writers(function_instance);
+
+ return 0;
+ }
+ // XXX MODULE LOCK HERE
+ return -EINVAL;
+}
+
+/*!
+ * @brief tty_l26_os_disable
+ * @param function_instance
+ * @return none
+ *
+ * Called by acm-fd when the function driver is disabled.
+ */
+void tty_l26_os_disable(struct usbd_function_instance *function_instance)
+{
+ struct acm_private *acm = function_instance ? function_instance->privdata : NULL;
+ int index = acm->index;
+ TRACE_MSG1(TTY,"acm: %x", acm);
+ function_table[index] = NULL;
+ // XXX MODULE UNLOCK HERE
+}
+
+/*! \var struct acm_os_ops tty_l26_os_ops
+ * \brief acm OS operations table
+ *
+ */
+
+
+struct acm_os_ops tty_l26_os_ops = {
+ .enable = tty_l26_os_enable,
+ .disable = tty_l26_os_disable,
+ .wakeup_opens = tty_l26_os_wakeup_opens,
+ .wakeup_state = tty_l26_os_wakeup_state,
+ .schedule_wakeup_writers = tty_l26_os_schedule_wakeup_writers,
+ .recv_space_available = tty_l26_os_recv_space_available,
+ .recv_chars = tty_l26_os_recv_chars,
+ .schedule_hangup = tty_l26_os_schedule_hangup,
+ .recv_start_bh = tty_l26_recv_start_bh,
+ //.comm_feature = tty_l26_os_comm_feature,
+ .line_coding = tty_l26_os_line_coding,
+ //.control_state = tty_l26_os_control_state,
+};
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+
+/* USB Module init/exit ************************************************************************ */
+
+/*!
+ * @brief tty_l26_init - module init
+ *
+ * This is called immediately after the module is loaded or during
+ * the kernel driver initialization if linked into the kernel.
+ * @param name
+ * @param minor_num
+ * @return int
+ */
+int tty_l26_init (char *name, int minor_num)
+{
+ int i;
+
+ TRACE_MSG2(TTY, "name: %s minor_num: %d", name, minor_num);
+
+
+ THROW_UNLESS (tty_table = (struct tty_struct **)
+ CKMALLOC(sizeof(struct tty_struct *)* minor_num ), error) ;
+
+ THROW_UNLESS (function_table = (struct usbd_function_instance **)
+ CKMALLOC(sizeof(struct usbd_function_instance *) * minor_num), error) ;
+
+ tty_minor_count = minor_num;
+
+#if defined(LINUX26)
+
+ THROW_UNLESS((tty_driver = alloc_tty_driver(minor_num)), error);
+
+ /* update init_termios and register as tty driver
+ */
+ tty_driver->owner = THIS_MODULE;
+ tty_driver->driver_name = name;
+ tty_driver->name = "acm";
+ tty_driver->major = ACM_TTY_MAJOR;
+ tty_driver->minor_start = 0;
+ tty_driver->minor_num = minor_num;
+ tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ tty_driver->subtype = SERIAL_TYPE_NORMAL;
+
+ tty_driver->flags = TTY_DRIVER_REAL_RAW;
+
+ #if defined(TTY_DRIVER_NO_DEVFS)
+ tty_driver->devfs_name = "tts/acm%d";
+ tty_driver->flags |= TTY_DRIVER_NO_DEVFS;
+// #warning "TTY_DRIVER_NO_DEVFS------------"
+ #endif /* defined(TTY_DRIVER_NO_DEVFS) */
+ // XXX Probably not correct config name
+ #if defined(TTY_DRIVER_DYNAMIC_DEV)
+ tty_driver->flags |= TTY_DRIVER_DYNAMIC_DEV;
+// #warning "TTY_DRIVER_DYNAMIC_DEV------------"
+ #endif /* defined(TTY_DRIVER_DYNAMIC_DEV) */
+ tty_driver->init_termios = tty_std_termios;
+ tty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+
+ tty_set_operations(tty_driver, &tty_ops);
+ tty_driver->tiocmget= tty_l26_tiocmget;
+ tty_driver->tiocmset= tty_l26_tiocmset;
+ THROW_IF(tty_register_driver(tty_driver), error);
+
+ for (i = 0; i < tty_minor_count; i++)
+ tty_register_device(tty_driver, i, NULL);
+
+
+ /* for (i = 0; i < minor_num; i++)
+ devfs_mk_cdev(MKDEV(ACM_TTY_MAJOR, i), S_IFCHR|S_IRUGO, (i & 1) ? "atcom/%d" : "acm/%d", i);*/
+
+#else /* defined(LINUX26) */
+
+ /* update init_termios and register as tty driver
+ */
+ tty_driver.magic = TTY_DRIVER_MAGIC;
+ tty_driver.driver_name = name;
+ tty_driver.name = "acm0";
+
+ tty_driver.major = ACM_TTY_MAJOR;
+ tty_driver.num = ACM_TTY_MINORS;
+ tty_driver.minor_start = 0;
+ tty_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ tty_driver.subtype = SERIAL_TYPE_NORMAL;
+ tty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+ tty_driver.init_termios = tty_std_termios;
+ tty_driver.init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
+
+ THROW_IF(tty_register_driver(&tty_driver), error);
+
+
+ for (i = 0; i < minor_num; i++)
+ tty_register_devfs(&tty_driver, 0, i);
+
+ TRACE_MSG0(TTY,"Register ok");
+
+#endif /* defined(LINUX26) */
+
+ CATCH(error) {
+ printk(KERN_ERR"%s: ERROR\n", __FUNCTION__);
+
+#if defined(LINUX26)
+ put_tty_driver(tty_driver);
+#endif /* defined(LINUX26) */
+
+ if (tty_table) LKFREE(tty_table);
+ if (function_table) LKFREE(function_table);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/*!
+ *@brief tty_l26_exit - module cleanup
+ *
+ * This is called prior to the module being unloaded.
+ */
+void tty_l26_exit (void)
+{
+ unsigned long flags;
+ int i;
+ //struct usbd_urb *urb;
+
+ /* Wake up any pending opens after setting the exiting flag. */
+ //tty_l26_private.exiting = 1;
+ //if (tty_l26_private.open_wait_count > 0)
+ //wake_up_interruptible(&tty_l26_private.open_wait);
+
+ /* verify no tasks are running */
+ //acm_wait_task(acm->function_instance, &acm->recv_tqueue);
+ //acm_wait_task(acm->function_instance, &tty_l26_private.wqueue);
+ //acm_wait_task(acm->function_instance, &tty_l26_private.hqueue);
+
+ /* de-register as tty and usb drivers */
+#if defined(LINUX26)
+
+ printk(KERN_INFO "tty_l26_exit!\n");
+ for (i = 0; i < tty_minor_count; i++)
+
+ tty_unregister_device(tty_driver, i);
+
+ // XXX blk_run_queues();
+ tty_unregister_driver(tty_driver);
+
+ put_tty_driver(tty_driver);
+
+ printk(KERN_INFO "after tty_unregister_driver!\n");
+#else
+ run_task_queue(&tq_timer);
+ tty_unregister_driver(&tty_driver);
+#endif
+
+ if (tty_table) LKFREE(tty_table);
+ if (function_table) LKFREE(function_table);
+ printk(KERN_INFO "tty_l26_exit!\n");
+
+
+}
diff --git a/drivers/otg/functions/acm/tty.h b/drivers/otg/functions/acm/tty.h
new file mode 100644
index 000000000000..936e597b9bf8
--- /dev/null
+++ b/drivers/otg/functions/acm/tty.h
@@ -0,0 +1,162 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/acm/tty.h
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/functions/acm/tty.h|20070911235625|36999
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/acm/tty.h
+ * @brief ACM Function Driver private defines
+ *
+ *
+ * This is an ACM Function Driver. The upper edge is exposed
+ * to the hosting OS as a Posix type character device. The lower
+ * edge implements the USB Device Stack API.
+ *
+ * These are emulated and set by the tty driver as appropriate
+ * to model a virutal serial port. The
+ *
+ * TIOCM_RNG RNG (Ring) not used
+ * TIOCM_LE DSR (Data Set Ready / Line Enable)
+ * TIOCM_DSR DSR (Data Set Ready)
+ * TIOCM_CAR DCD (Data Carrier Detect)
+ * TIOCM_CTS CTS (Clear to Send)
+ * TIOCM_CD TIOCM_CAR
+ * TIOCM_RI TIOCM_RNG
+ *
+ * These are set by the application:
+ *
+ * TIOCM_DTR DTR (Data Terminal Ready)
+ * TIOCM_RTS RTS (Request to Send)
+ *
+ * TIOCM_LOOP Set into loopback mode
+ * TIOCM_OUT1 Not used.
+ * TIOCM_OUT2 Not used.
+ *
+ *
+ * The following File and termio c_cflags are used:
+ *
+ * O_NONBLOCK don't block in tty_open()
+ * O_EXCL don't allow more than one open
+ *
+ * CLOCAL ignore DTR status
+ * CBAUD send break if set to B0
+ *
+ * Virtual NULL Modem
+ *
+ * Peripheral to Host via Serial State Notification (write ioctls)
+ *
+ * Application TIOCM Null Modem ACM (DCE) Notificaiton Host (DTE)
+ * -------------------------------------------------------------------------------------------------------------
+ * Request to send TIOCM_RTS RTS -> CTS CTS Not Available Clear to Send (N/A)
+ * Data Terminal Ready TIOCM_DTR DTR -> DSR DSR bTxCarrier Data Set Ready
+ * DTR -> DCD DCD bRXCarrier Carrier Detect
+ * Ring Indicator TIOCM_OUT1 OUT1 -> RI RI bRingSignal Ring Indicator
+ * Overrun TIOCM_OUT2 OUT2 -> Overrun Overrun bOverrun Overrun
+ * Send Break B0 B0 -> Break Break bBreak Break Received
+ * -------------------------------------------------------------------------------------------------------------
+ *
+ *
+ * Host to Peripheral via Set Control Line State
+ *
+ * Host (DTE) Line State ACM (DCE) Null Modem TIOCM Peripheral (DTE)
+ * -------------------------------------------------------------------------------------------------------------
+ * Data Terminal Ready D0 DTR DTR -> DSR TIOCM_DSR Data Set Ready
+ * DTR -> DCD TIOCM_CAR Carrier Detect
+ * Request To Send D1 RTS RTS -> CTS TIOCM_CTS Clear to Send
+ * -------------------------------------------------------------------------------------------------------------
+ *
+ *
+ * @ingroup TTYFunction
+ * @ingroup LINUXAPI
+ */
+
+extern struct usbd_function_driver tty_function_driver;
+
+
+#define TTYFD_OPENED (1 << 0) /*! OPENED flag */
+#define TTYFD_CLOCAL (1 << 1) /*! CLOCAL flag */
+//#define TTYFD_LOOPBACK (1 << 2) /*! LOOPBACK flag */
+#define TTYFD_EXCLUSIVE (1 << 3) /*! EXCLUSIVE flag */
+#define TTYFD_THROTTLED (1 << 4) /*! THROTTLED flag */
+
+#define TTY_OVERFLOW_SIZE 4096
+
+/*! @struct os_private tty.h "otg/functions/acm/tty.h"
+ */
+struct os_private {
+
+ //struct tty_driver *tty_driver; /*!< tty structure */
+ int tty_driver_registered; /*!< non-zero if tty_driver registered */
+ int usb_driver_registered; /*!< non-zero if usb function registered */
+
+ int index;
+
+ struct otg_workitem *wakeup_workitem;
+ struct otg_workitem *hangup_workitem;
+ u32 flags; /*!< flags */
+
+ u32 tiocm; /*!< tiocm settings */
+
+ struct serial_struct serial_struct; /*!< serial structure used for TIOCSSERIAL and TIOCGSERIAL */
+
+ wait_queue_head_t tiocm_wait;
+ u32 tiocgicount;
+
+ //u32 c_cflag;
+
+ wait_queue_head_t open_wait; /*! wait queue for blocking open*/
+ int exiting; /*! True if module exiting */
+
+ wait_queue_head_t send_wait; /*! wait queue for blocking open*/
+ int sending;
+
+ struct otg_workitem *recv_workitem;
+ atomic_t used;
+
+ u8 tty_overflow_buffer[TTY_OVERFLOW_SIZE];
+ atomic_t tty_overflow_used;
+ int minor_num;
+
+ //u8 tty_inflow_buffer[TTY_FLIPBUF_SIZE];
+ //int tty_inflow_used;
+};
+
+/*! @name modules initialization and unload functions */
+
+//@{
+//
+int tty_l26_init(char *, int);
+void tty_l26_exit(void);
+
+int tty_if_init(void);
+void tty_if_exit(void);
+
+int dun_if_init(void);
+void dun_if_exit(void);
+
+int atcom_if_init(void);
+void atcom_if_exit(void);
+
+int obex_if_init(void);
+void obex_if_exit(void);
+
+//@}
diff --git a/drivers/otg/functions/generic/Kconfig b/drivers/otg/functions/generic/Kconfig
new file mode 100644
index 000000000000..f554be45e056
--- /dev/null
+++ b/drivers/otg/functions/generic/Kconfig
@@ -0,0 +1,347 @@
+# @(#) sp@belcarra.com|otg/functions/generic/Kconfig|20070417200305|49656
+menu "OTG Generic function"
+ depends on OTG
+
+config OTG_GENERIC
+ tristate "Generic Function"
+ depends on OTG
+ default OTG
+ ---help---
+ This acts as a generic function, allowing selection of class
+ and interface function drivers to create a specific configuration.
+
+menu "OTG Generic Composite function options"
+ depends on OTG && OTG_GENERIC
+
+config OTG_GENERIC_VENDORID
+ hex "VendorID (hex value)"
+ depends on OTG && OTG_GENERIC
+ default "0x15ec"
+
+config OTG_GENERIC_PRODUCTID
+ hex "ProductID (hex value)"
+ depends on OTG && OTG_GENERIC
+ default "0xf010"
+
+config OTG_GENERIC_BCDDEVICE
+ hex "bcdDevice (binary-coded decimal)"
+ depends on OTG && OTG_GENERIC
+ default "0x0100"
+
+config OTG_GENERIC_MANUFACTURER
+ string "iManufacturer (string)"
+ depends on OTG && OTG_GENERIC
+ default "Belcarra"
+
+config OTG_GENERIC_PRODUCT_NAME
+ string "iProduct (string)"
+ depends on OTG && OTG_GENERIC
+ default "Generic Composite"
+
+choice
+ depends on OTG && OTG_GENERIC
+ prompt "Default Composite Driver Configuration"
+
+ config OTG_GENERIC_CONFIG_NONE
+ bool 'none'
+ help
+ No pre-defined selection, use the OTG_GENERIC_CONFIG_NAME instead.
+
+ config OTG_GENERIC_CONFIG_MOUSE
+ bool 'mouse'
+ depends on OTG_MOUSE
+ help
+ Configure the mouse driver in a single function non-composite configuration.
+
+ config OTG_GENERIC_CONFIG_NET_EEM
+ bool 'cdc-eem'
+ depends on OTG_NETWORK_EEM
+ help
+ Configure the network driver as a single function CDC EEM non-composite device
+
+ config OTG_GENERIC_CONFIG_NET_BLAN
+ bool 'net-blan'
+ depends on OTG_NETWORK_BLAN
+ help
+ Configure the network driver in a single function non-composite BLAN configuration.
+
+
+ config OTG_GENERIC_CONFIG_NET_CDC
+ bool 'net-cdc'
+ depends on OTG_NETWORK_ECM
+ help
+ Configure the network driver in a single function non-composite CDC configuration.
+
+ config OTG_GENERIC_CONFIG_NET_SAFE
+ bool 'net-safe'
+ depends on OTG_NETWORK_SAFE
+ help
+ Configure the network driver in a single function non-composite SAFE configuration.
+
+
+ config OTG_GENERIC_CONFIG_ACM_TTY
+ bool 'acm-tty'
+ depends on OTG_ACM
+ help
+ Configure the acm driver in a single function non-composite TTY configuration.
+
+
+ config OTG_GENERIC_CONFIG_MSC
+ bool 'msc'
+ depends on OTG_MSC
+ help
+ Configure the msc driver in a single function non-composite configuration.
+
+ config OTG_GENERIC_CONFIG_HID2_NON_IAD
+ bool 'hid2'
+ depends on OTG_MOUSE
+ help
+ Configure a non-IAD composite device with two HID interfaces.
+
+ config OTG_GENERIC_CONFIG_HID2
+ bool 'hid2-iad'
+ depends on OTG_MOUSE
+ help
+ Configure an IAD composite device with two HID interfaces.
+
+ config OTG_GENERIC_CONFIG_HID_MSC
+ bool 'msc-hid'
+ depends on OTG_MSC && OTG_MOUSE
+ help
+ Configure a non-IAD composite device with mass storage and HID interface.
+
+
+endchoice
+
+ #config OTG_GENERIC_CONFIG_NAME
+ # string "Composite Configuration (string)"
+ # depends on OTG && OTG_GENERIC && OTG_GENERIC_CONFIG_NONE
+ # default ""
+ # ---help---
+ # Name of predefined configuration to be enabled, note that
+ # if this is not defined (empty string) then the first available
+ # configuration will be used (assuming that the required interface
+ # function drivers are available.
+
+ #config OTG_NETWORK_VENDORID
+ # hex "Network VendorID (hex value)"
+ # depends on OTG && OTG_NETWORK
+ # default "0x15ec"
+ # ---help---
+ # The USB Peripheral Vendor ID. This should be set to your assigned
+ # USB Vendor ID (www.usb.org). The USB Host will use this and the
+ # Product ID to find and load an appropriate device driver.
+
+ #comment --
+ #comment "USBOTG Network Device Product ID"
+
+ ######################################################################################################3
+ #
+ # Product ID's for generic cf, non-composite, these are assigned to f00N
+ #
+
+ #config OTG_NETWORK_PRODUCT_NAME
+ # string "iProduct (string)"
+ # depends on OTG && OTG_NETWORK
+ # default "Belcarra Network Device"
+ # ---help---
+ # This will be used as the iProduct string descriptor.
+
+ config OTG_GENERIC_PRODUCTID_CDCEEM
+ hex "Generic CDC-EEM ProductID (hex value)"
+ depends on OTG && OTG_NETWORK
+ default "0xf001"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_SERIAL
+ hex "Generic Serial Port CDC-ACM ProductID (hex value)"
+ depends on OTG && OTG_ACM
+ default "0xf002"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_HID
+ hex "Generic HID ProductID (hex value)"
+ depends on OTG && OTG_MOUSE
+ default "0xf003"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_MODEM
+ hex "Generic Modem CDC-ACM ProductID (hex value)"
+ depends on OTG && OTG_ACM
+ default "0xf004"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_CDCECM
+ hex "Generic CDC-ECM ProductID (hex value)"
+ depends on OTG && OTG_NETWORK
+ default "0xf005"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_MASS
+ hex "Generic MASS ProductID (hex value)"
+ depends on OTG && OTG_MSC
+ default "0xf006"
+ ---help---
+ The USB Peripheral Product ID. This should be set to your a
+ unique value for this product. The USB Host will use this and the
+ Vendor ID to find and load an appropriate device driver.
+ #config OTG_GENERIC_PRODUCTID_COMPOSITE
+ # hex "Composite ProductID (hex value)"
+ # depends on OTG
+ # default "0xf008"
+ # ---help---
+ # The USB Peripheral Product ID. This should be set to your a
+ # unique value for this product. The USB Host will use this and the
+ # Vendor ID to find and load an appropriate device driver.
+
+ #####################################################################################################3
+ #
+ # Composite ID's are assigned to
+ #
+ # c00N
+ #
+
+ # c000
+ config OTG_GENERIC_PRODUCTID_HID2_NON_IAD
+ hex "HID2 Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE
+ default "0xc000"
+ ---help---
+
+ hid2_non_iad implements a two interface composite device using
+ with two mouse interfaces.
+
+ This configuration does not use IAD (Interface Association Descriptors.
+ It will work with Win2k and newer versions of Windows. But
+ has been deprecated by the introduction of IAD. IAD support
+ is available in WinXP and newer versions of Windows.
+
+ The USB Peripheral Product ID for the generic hid2 configuration.
+ This should be set to your a unique value for this product.
+ The USB Host will use this and the Vendor ID to find and
+ load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_HID2
+ hex "HID2-IAD Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE
+ default "0xc001"
+ ---help---
+ hid2 implements a two interface composite device using
+ with two mouse interfaces.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+ It will work with WinXP and newer verions of Windows.
+
+ The USB Peripheral Product ID for the hid2 iad
+ configuration. This should be set to your a unique value
+ for this product. The USB Host will use this and the Vendor
+ ID to find and load an appropriate device driver.
+
+ # c002
+ config OTG_GENERIC_PRODUCTID_HID_MSC
+ hex "MSC-HID Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE && OTG_MSC
+ default "0xc002"
+ ---help---
+ hid_msc implements a two interface composite device with
+ the mass storage and mouse interfaces.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+ It will work with WinXP and newer verions of Windows.
+
+ The USB Peripheral Product ID for the generic msc-hid
+ configuration. This should be set to your a unique value for
+ this product. The USB Host will use this and the Vendor ID
+ to find and load an appropriate device driver.
+
+
+ # c003
+ config OTG_GENERIC_PRODUCTID_HID_BLAN
+ hex "HID-BLAN Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE && OTG_NETWORK_BLAN
+ default "0xc003"
+ ---help---
+ hid_blan implements a two interface composite device with
+ a mouse and blan network interface.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+
+ The USB Peripheral Product ID for the hid-blan
+ configuration. This should be set to your a unique value for
+ this product. The USB Host will use this and the Vendor ID
+ to find and load an appropriate device driver.
+
+
+ # c004
+ config OTG_GENERIC_PRODUCTID_HID_ECM
+ hex "HID-ECM Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE && OTG_NETWORK_ECM
+ default "0xc004"
+ ---help---
+ hid_ecm implements a two interface composite device with
+ a mouse and cdc-ecm network interface.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+
+ The USB Peripheral Product ID for the hid-ecm
+ configuration. This should be set to your a unique value for
+ this product. The USB Host will use this and the Vendor ID
+ to find and load an appropriate device driver.
+
+
+ # c005/c006 serial/modem
+ config OTG_GENERIC_PRODUCTID_HID_SERIAL
+ hex "HID-SERIAL Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE && OTG_ACM
+ default "0xc005"
+ ---help---
+ hid_serial implements a two interface composite device with
+ a mouse and cdc-acm serial interface.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+
+ The USB Peripheral Product ID for the hid-serial
+ configuration. This should be set to your a unique value for
+ this product. The USB Host will use this and the Vendor ID
+ to find and load an appropriate device driver.
+
+ config OTG_GENERIC_PRODUCTID_HID_MODEM
+ hex "HID-MODEM Composite ProductID (hex value)"
+ depends on OTG && OTG_MOUSE && OTG_ACM
+ default "0xc006"
+ ---help---
+ hid_serial implements a two interface composite device with
+ a mouse and cdc-acm modem interface.
+
+ This configuration uses IAD (Interface Association Descriptors.)
+
+ The USB Peripheral Product ID for the hid-modem
+ configuration. This should be set to your a unique value for
+ this product. The USB Host will use this and the Vendor ID
+ to find and load an appropriate device driver.
+
+
+ #config OTG_NETWORK_BCDDEVICE
+ # hex "Network bcdDevice (binary-coded decimal)"
+ # depends on OTG && OTG_NETWORK
+ # default "0x0100"
+
+
+endmenu
+
+endmenu
diff --git a/drivers/otg/functions/generic/Makefile b/drivers/otg/functions/generic/Makefile
new file mode 100644
index 000000000000..417ac2653689
--- /dev/null
+++ b/drivers/otg/functions/generic/Makefile
@@ -0,0 +1,26 @@
+# Generic Composite Driver
+#
+# @(#) balden@belcarra.com|otg/functions/generic/Makefile-l26|20060419204257|43174
+# Copyright (c) 2004-2005 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+ifeq ($(CONFIG_OTG_GENERIC),m)
+generic_cf-objs := generic-cf.o generic.o
+else
+generic_cf-objs := generic-cf.o
+obj-y += generic.o
+endif
+
+extra-y := generic.o
+
+
+generic_cl-objs := generic-cl.o
+
+obj-$(CONFIG_OTG_GENERIC) += generic_cf.o
+obj-$(CONFIG_OTG_GENERIC) += generic_cl.o
+
+
+OTG=$(TOPDIR)/drivers/otg
+OTGCORE_DIR=$(OTG)/otgcore
+EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
+EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
diff --git a/drivers/otg/functions/generic/generic-cf.c b/drivers/otg/functions/generic/generic-cf.c
new file mode 100644
index 000000000000..e168d011120b
--- /dev/null
+++ b/drivers/otg/functions/generic/generic-cf.c
@@ -0,0 +1,601 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/generic/generic-cf.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/generic/generic-cf.c|20070425221028|63290
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/functions/generic/generic-cf.c
+ * @brief Generic Configuration Function Driver
+ *
+ * This implements a generic composite function
+ *
+ * - idVendor 16 bit word
+ * - idProduct 16 bit word
+ * - bcd_device 16 bit word
+ *
+ * - bDeviceClass byte
+ * - device_sub-class byte
+ * - bcdDevice byte
+ *
+ * - vendor string
+ * - iManufacturer string
+ * - product string
+ * - serial string
+ *
+ * - power byte
+ * - remote_wakeup bit
+ *
+ * - functions string
+ *
+ *
+ * The functions string would contain a list of names, the first would
+ * be the composite function driver, the rest would be the interface
+ * functions. For example:
+ *
+ *
+ * "cmouse-cf mouse-if"
+ * "mcpc-cf dun-if dial-if obex-if dlog-if"
+ *
+ * There are also a set of pre-defined configurations that
+ * can be loaded singly or in toto.
+ *
+ * @ingroup GenericFunction
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-cdc.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-utils.h>
+
+#include "generic.h"
+//#include "generic.c"
+
+/* Module Parameters ******************************************************** */
+/* !
+ * @name XXXXX MODULE Parameters
+ */
+/* ! @{ */
+
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Generic Composite Function");
+
+
+MOD_PARM_STR (driver_name, "Driver Name", NULL);
+
+MOD_PARM_INT(idVendor, "Device Descriptor idVendor field", 0);
+MOD_PARM_INT(idProduct, "Device Descriptor idProduct field", 0);
+MOD_PARM_INT(bcddevice, "Device Descriptor bcdDevice field", 0);
+
+MOD_PARM_INT (bDeviceClass, "Device Descriptor bDeviceClass field", 0);
+MOD_PARM_INT (bDeviceSubClass, "Device Descriptor bDeviceSubClass field", 0);
+MOD_PARM_INT (bDeviceProtocol, "Device Descriptor bDeviceProtocol field", 0);
+MOD_PARM_INT (bcdDevice, "Device Descriptor bcdDevice field", 0);
+
+MOD_PARM_STR (iConfiguration, "Configuration Descriptor iConfiguration field", NULL);
+MOD_PARM_STR (iManufacturer, "Device Descriptor iManufacturer field", NULL);
+MOD_PARM_STR (iProduct, "Device Descriptor iProduct field", NULL);
+MOD_PARM_STR (iSerialNumber, "Device Descriptor iSerialNumber field", NULL);
+
+MOD_PARM_STR (class_name, "Class Function Name", NULL);
+MOD_PARM_STR (interface_names, "Interface Function Names List", NULL);
+
+MOD_PARM_STR (config_name, "Pre-defined Configuration", NULL);
+MOD_PARM_BOOL (load_all, "Load all pre-defined Configurations", 1);
+MOD_PARM_BOOL (override, "Override idVendor and idProduct", 0);
+
+/* Setup the pre-defined configuration name from configuration
+ * option if available.
+ *
+ * N.B. This list needs to be synchronized with both pre-defined
+ * configurations (see below) and the Kconfig list
+ */
+char *generic_predefined_configuration = "";
+
+
+/* ! *} */
+
+
+/* Setup the pre-defined configuration name from configuration
+ * option if available.
+ *
+ * N.B. This list needs to be synchronized with both pre-defined
+ * configurations (see below) and the Kconfig list
+ */
+#ifdef CONFIG_OTG_GENERIC_CONFIG_MOUSE
+static char default_predefined_configuration[] = "hid";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_NET_BLAN)
+static char default_predefined_configuration[] = "mdlm-blan";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_NET_SAFE)
+static char default_predefined_configuration[] = "mdlm-safe";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_NET_CDC)
+static char default_predefined_configuration[] = "cdc-ecm";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_NET_EEM)
+static char default_predefined_configuration[] = "cdc-eem";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_ACM_TTY)
+static char default_predefined_configuration[] = "serial";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_MSC)
+static char default_predefined_configuration[] = "mass";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_HID2)
+static char default_predefined_configuration[] = "hid2";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_HID2_NON_IAD)
+static char default_predefined_configuration[] = "hid2-non_iad";
+#elif defined(CONFIG_OTG_GENERIC_CONFIG_MSC_HID)
+static char default_predefined_configuration[] = "msc-hid";
+#else
+static char default_predefined_configuration[] = "";
+#endif
+
+static char *generic_config_name(void){
+ if(MODPARM(config_name) && strlen(MODPARM(config_name))) return MODPARM(config_name);
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, default_predefined_configuration);
+ return default_predefined_configuration;
+}
+
+static int preset_config_name(void){
+ return (strlen(generic_config_name()) > 0);
+}
+
+
+/* Pre-defined configurations *********************************************** */
+
+static struct generic_config generic_cf_configs[] = {
+ #if defined(CONFIG_OTG_ACM) || defined(CONFIG_OTG_ACM_MODULE) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "serial", }, },
+ .interface_names = "tty-if",
+ .configuration_description = {
+ .iConfiguration = "acm-tty",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_SERIAL),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " Serial", },
+
+ },
+ {
+ .composite_driver = { .driver = { .name = "modem", }, },
+ .interface_names = "tty-if",
+ .configuration_description = {
+ .iConfiguration = "acm-tty",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_MODEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " Serial", },
+
+ },
+ #endif /* defined(CONFIG_OTG_ACM) */
+ #if defined(CONFIG_OTG_NETWORK_EEM) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "cdc-eem", }, },
+ .interface_names = "cdc-eem-if",
+ .configuration_description = {
+ .iConfiguration = "cdc-eem",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = COMMUNICATIONS_EEM_SUBCLASS,
+ .bDeviceProtocol = COMMUNICATIONS_EEM_PROTOCOL,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCEEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " CDC-EEM", }
+ },
+ #endif /* defined(CONFIG_OTG_NETWORK_EEM) */
+ #if defined(CONFIG_OTG_NETWORK_ECM) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "cdc-ecm", }, },
+ .interface_names = "cdc-ecm-if",
+ .configuration_description = {
+ .iConfiguration = "cdc-ecm",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCECM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " CDC-ECM", },
+ },
+ #endif /* defined(CONFIG_OTG_NETWORK_CDC) */
+ #if defined(CONFIG_OTG_MOUSE) || defined(CONFIG_OTG_MOUSE_MODULE) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "hid", }, },
+ .interface_names = "mouse-if",
+ .configuration_description = {
+ .iConfiguration = "mouse",
+ },
+ .device_description = {
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " Mouse", },
+ },
+ #endif /* defined(CONFIG_OTG_MOUSE) */
+ #if defined(CONFIG_OTG_MSC) || defined(CONFIG_OTG_MSC_MODULE) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "mass", }, },
+ .interface_names = "msc-if",
+ .configuration_description = {
+ .iConfiguration = "msc",
+ },
+ .device_description = {
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_MASS),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " Mass Storage", },
+ },
+ #endif /* defined(CONFIG_OTG_MSC) */
+
+ /* deprecated - use cdc-eem */
+ #if defined(CONFIG_OTG_NETWORK_BLAN) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "network", }, },
+ .interface_names = "net-blan-if",
+ .configuration_description = {
+ .iConfiguration = "net-blan",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCEEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " MDLM-BLAN", },
+ },
+ {
+ .composite_driver = { .driver = { .name = "mdlm-blan", }, },
+ .interface_names = "net-blan-if",
+ .configuration_description = {
+ .iConfiguration = "net-blan",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCEEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " MDLM-BLAN", },
+ },
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN) */
+
+ #if defined(CONFIG_OTG_NETWORK_SAFE) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "mdlm-safe", }, },
+ .interface_names = "net-safe-if",
+ .configuration_description = {
+ .iConfiguration = "net-safe",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCEEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " MDLM-SAFE", },
+ },
+ #endif /* defined(CONFIG_OTG_NETWORK_SAFE) */
+
+ #if defined(CONFIG_OTG_NETWORK_BASIC) || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "net-basic", }, },
+ .interface_names = "net-basic-if",
+ .configuration_description = {
+ .iConfiguration = "net-basic",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_CDCEEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " NET-BASIC", },
+ },
+ #endif /* defined(CONFIG_OTG_NETWORK_BASIC) */
+
+ #if defined(CONFIG_OTG_MOUSE) || defined(CONFIG_OTG_MOUSE_MODULE)
+ {
+ .composite_driver.driver.name = "hid2-non-iad",
+ .interface_names = "mouse-if:mouse-if",
+ .configuration_description.iConfiguration = "mouse2",
+ .device_description.bDeviceClass = 0,
+ .device_description.bDeviceSubClass = 0,
+ .device_description.bDeviceProtocol = 0,
+ .device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID2),
+ .device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .device_description.iProduct = CONFIG_OTG_GENERIC_MANUFACTURER " USB HID (Mouse2)",
+ },
+ {
+ .composite_driver.driver.name = "hid2",
+ .interface_names = "mouse-if:mouse-if",
+ .configuration_description.iConfiguration = "mouse2",
+ .device_description.bDeviceClass = USB_CLASS_MISC,
+ .device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID2),
+ .device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .device_description.iProduct = CONFIG_OTG_GENERIC_MANUFACTURER " USB HID (Mouse2) IAD",
+ },
+ #endif /* defined(CONFIG_OTG_MOUSE) */
+
+ #if (defined(CONFIG_OTG_MSC_MODULE) || defined(CONFIG_OTG_MSC)) && \
+ (defined(CONFIG_OTG_MOUSE_MODULE) || defined(CONFIG_OTG_MOUSE))
+ {
+ .composite_driver.driver.name = "hid-msc",
+ .interface_names = "mouse-if:msc-if",
+ .configuration_description.iConfiguration = "mouse-msc",
+ .device_description.bDeviceClass = USB_CLASS_MISC,
+ .device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID_MSC),
+ .device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .device_description.iProduct = CONFIG_OTG_GENERIC_MANUFACTURER " HID-MSC Composite IAD",
+ },
+ #endif /* defined(CONFIG_OTG_MSC) && defined(CONFIG_OTG_MOUSE)) */
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN) && (defined(CONFIG_OTG_MOUSE_MODULE) || defined(CONFIG_OTG_MOUSE))
+ {
+ .composite_driver.driver.name = "hid-blan",
+ .interface_names = "mouse-if:net-blan-if",
+ .configuration_description.iConfiguration = "hid-blan",
+ .device_description.bDeviceClass = USB_CLASS_MISC,
+ .device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID_BLAN),
+ .device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .device_description.iProduct = CONFIG_OTG_GENERIC_MANUFACTURER " HID-BLAN Composite IAD",
+ },
+ #endif /* defined(CONFIG_OTG_MSC) && defined(CONFIG_OTG_MOUSE)) */
+ #if defined(CONFIG_OTG_NETWORK_ECM) && (defined(CONFIG_OTG_MOUSE_MODULE) || defined(CONFIG_OTG_MOUSE))
+ {
+ .composite_driver.driver.name = "hid-ecm",
+ .interface_names = "mouse-if:cdc-ecm-if",
+ .configuration_description.iConfiguration = "hid-ecm",
+ .device_description.bDeviceClass = USB_CLASS_MISC,
+ .device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID_ECM),
+ .device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .device_description.iProduct = CONFIG_OTG_GENERIC_MANUFACTURER " HID-BLAN Composite IAD",
+ },
+ #endif /* defined(CONFIG_OTG_MSC) && defined(CONFIG_OTG_MOUSE)) */
+
+ #if ( \
+ (defined(CONFIG_OTG_MOUSE_MODULE) || defined(CONFIG_OTG_MOUSE)) && \
+ (defined(CONFIG_OTG_ACM) || defined(CONFIG_OTG_ACM_MODULE))) \
+ || defined(_OTG_DOXYGEN)
+ {
+ .composite_driver = { .driver = { .name = "hid-serial", }, },
+ .interface_names = "hid:tty-if",
+ .configuration_description = {
+ .iConfiguration = "hid-acm-tty",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID_SERIAL),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " HID-Serial", },
+
+ },
+ {
+ .composite_driver = { .driver = { .name = "hid-modem", }, },
+ .interface_names = "hid:tty-if",
+ .configuration_description = {
+ .iConfiguration = "hid-acm-tty",
+ },
+ .device_description = {
+ .bDeviceClass = COMMUNICATIONS_DEVICE_CLASS,
+ .bDeviceSubClass = 2,
+ .bDeviceProtocol = 0,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID_HID_MODEM),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME " HID-Serial", },
+
+ },
+ #endif /* defined(CONFIG_OTG_ACM) && defined(CONFIG_OTG_MOUSE) */
+
+
+ { },
+};
+
+static struct generic_config generic_config = {
+ .composite_driver = { .driver = { .name = "generic", }, },
+ .device_description = {
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_PRODUCTID),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_GENERIC_BCDDEVICE),
+ .iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER,
+ .iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME, },
+};
+
+
+
+
+static otg_tag_t GENERIC;
+
+/*!
+ * generic_cf_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the generic driver with the USB Device software.
+ *
+ */
+int generic_cf_modinit (void)
+{
+ /* load config or configs
+ */
+
+ #if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE)
+ GENERIC = otg_trace_obtain_tag(NULL, "generic-cf");
+ #endif
+
+ if (preset_config_name() || MODPARM(load_all)) {
+ TRACE_MSG0(GENERIC, "PRESET");
+ return generic_modinit(generic_cf_configs,
+ generic_config_name(),
+ MODPARM(override),
+ MODPARM(idVendor),
+ MODPARM(idProduct),
+ MODPARM(iSerialNumber),
+ GENERIC);
+ }
+ else {
+ struct generic_config *config = &generic_config;
+
+ TRACE_MSG0(GENERIC, "SEARCH");
+
+ printk (KERN_INFO "%s: idVendor: %04x idProduct: %04x\n", __FUNCTION__, MODPARM(idVendor), MODPARM(idProduct));
+ printk (KERN_INFO "%s: class_name: \"%s\" _interface_names: \"%s\"\n",
+ __FUNCTION__, MODPARM(class_name), MODPARM(interface_names));
+
+ if (MODPARM(driver_name) && strlen(MODPARM(driver_name)))
+ config->composite_driver.driver.name = MODPARM(driver_name);
+
+ if (MODPARM(class_name) && strlen(MODPARM(class_name)))
+ config->class_name = MODPARM(class_name);
+
+ if (MODPARM(interface_names) && strlen(MODPARM(interface_names)))
+ config->interface_names = MODPARM(interface_names);
+
+ if (MODPARM(iConfiguration) && strlen(MODPARM(iConfiguration)))
+ config->configuration_description.iConfiguration = MODPARM(iConfiguration);
+
+ if (MODPARM(bDeviceClass))
+ config->device_description.bDeviceClass = MODPARM(bDeviceClass);
+
+ if (MODPARM(bDeviceSubClass))
+ config->device_description.bDeviceSubClass = MODPARM(bDeviceSubClass);
+
+ if (MODPARM(bDeviceProtocol))
+ config->device_description.bDeviceProtocol = MODPARM(bDeviceProtocol);
+
+ if (MODPARM(idVendor))
+ config->device_description.idVendor = MODPARM(idVendor);
+ else
+ config->device_description.idVendor = CONFIG_OTG_GENERIC_VENDORID;
+
+ if (MODPARM(idProduct))
+ config->device_description.idProduct = MODPARM(idProduct);
+ else
+ config->device_description.idProduct = CONFIG_OTG_GENERIC_PRODUCTID;
+
+ if (MODPARM(bcdDevice))
+ config->device_description.bcdDevice = MODPARM(bcdDevice);
+ else
+ config->device_description.bcdDevice = CONFIG_OTG_GENERIC_BCDDEVICE;
+
+ if (MODPARM(iManufacturer) && strlen(MODPARM(iManufacturer)))
+ config->device_description.iManufacturer = MODPARM(iManufacturer);
+ else
+ config->device_description.iManufacturer = CONFIG_OTG_GENERIC_MANUFACTURER;
+
+ if (MODPARM(iProduct) && strlen(MODPARM(iProduct)))
+ config->device_description.iProduct = MODPARM(iProduct);
+ else
+ config->device_description.iProduct = CONFIG_OTG_GENERIC_PRODUCT_NAME;
+
+ if (MODPARM(iSerialNumber) && strlen(MODPARM(iSerialNumber))){
+ config->device_description.iSerialNumber = MODPARM(iSerialNumber);
+ }
+ if (MODPARM(interface_names))
+ config->interface_names = MODPARM(interface_names);
+
+ generic_register(config, NULL, FALSE, 0, 0, NULL, GENERIC);
+ return 0;
+ }
+}
+
+module_init (generic_cf_modinit);
+
+/*!
+ * generic_cf_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ * @return void
+ */
+void generic_cf_modexit (void)
+{
+ generic_modexit(generic_cf_configs, GENERIC);
+
+ if (generic_config.registered)
+ usbd_deregister_composite_function (&generic_config.composite_driver);
+
+ if (generic_config.interface_list)
+ LKFREE(generic_config.interface_list);
+
+ otg_trace_invalidate_tag(GENERIC);
+
+}
+#if OTG_EPILOGUE
+module_exit (generic_cf_modexit);
+#endif
diff --git a/drivers/otg/functions/generic/generic-cl.c b/drivers/otg/functions/generic/generic-cl.c
new file mode 100644
index 000000000000..15ff2fdb33e7
--- /dev/null
+++ b/drivers/otg/functions/generic/generic-cl.c
@@ -0,0 +1,169 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/generic/generic-cl.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/generic/generic-cl.c|20070425221028|46643
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @defgroup GenericClass Generic Class Function
+ * @ingroup ClassFunctions
+ */
+
+/*!
+ * @file otg/functions/generic/generic-cl.c
+ * @brief Generic Class Function Driver
+ *
+ * This implements a generic NULL class function driver.
+ *
+ * @ingroup GenericClass
+ */
+
+#include <otg/otg-compat.h>
+
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+#include "generic.h"
+
+/* Module Parameters ******************************************************** */
+/* !
+ * @name XXXXX MODULE Parameters
+ */
+/* ! @{ */
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Belcarra Generic NULL Class Function");
+
+otg_tag_t GCLASS;
+
+/* ! *} */
+
+/* ********************************************************************************************* */
+/*!
+ * generic_cl_device_request - called to process a request to endpoint or interface
+ * @param function
+ * @param request
+ * @return non-zero for failure, will cause endpoint zero stall
+ */
+static int generic_cl_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ TRACE_MSG5(GCLASS, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x",
+ request->bmRequestType, request->bRequest, request->wValue,
+ request->wIndex, request->wLength);
+
+ return -EINVAL;
+}
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations generic_function_ops;
+
+/*! generic_class_driver - USB Device Core function driver definition
+ */
+struct usbd_class_driver generic_class_driver;
+
+#else /* defined(OTG_C99) */
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations generic_function_ops = {
+ .device_request = generic_cl_device_request, /*!< called for each received device request */
+};
+
+/*! generic_class_driver - USB Device Core function driver definition
+ */
+struct usbd_class_driver generic_class_driver = {
+ .driver.name = "generic-class", /*!< driver name */
+ .driver.fops = &generic_function_ops, /*!< operations table */
+};
+#endif /* defined(OTG_C99) */
+
+
+/* USB Module init/exit ***************************************************** */
+
+//#if OTG_EPILOGUE
+/*!
+ * generic_cl_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ * @return none
+ */
+static void generic_cl_modexit (void)
+{
+ usbd_deregister_class_function (&generic_class_driver);
+ otg_trace_invalidate_tag(GCLASS);
+}
+
+module_exit (generic_cl_modexit);
+//#endif
+
+/*!
+ * generic_cl_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the generic driver with the USB Device software.
+ *
+ */
+static int generic_cl_modinit (void)
+{
+ #if !defined(OTG_C99)
+ /*! function_ops - operations table for the USB Device Core
+ */
+ ZERO(generic_function_ops);
+ generic_function_ops.device_request=generic_cl_device_request; /*! called for each received device request */
+
+ /*! class_driver - USB Device Core function driver definition
+ */
+ ZERO(generic_class_driver);
+ generic_class_driver.driver.name = "generic-class"; /*! driver name */
+ generic_class_driver.driver.fops = &generic_function_ops; /*! operations table */
+ #endif /* defined(OTG_C99) */
+
+ GCLASS = otg_trace_obtain_tag(NULL, "generic-cf");
+
+ // register as usb function driver
+ TRACE_MSG0(GCLASS, "REGISTER CLASS");
+ THROW_IF (usbd_register_class_function (&generic_class_driver, "generic-class", NULL), error);
+
+ TRACE_MSG0(GCLASS, "REGISTER FINISHED");
+
+ CATCH(error) {
+ generic_cl_modexit();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+module_init (generic_cl_modinit);
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/generic/generic.c b/drivers/otg/functions/generic/generic.c
new file mode 100644
index 000000000000..d3eaa43433dc
--- /dev/null
+++ b/drivers/otg/functions/generic/generic.c
@@ -0,0 +1,291 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/generic/generic-cf.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/generic/generic.c|20070425221028|47281
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/generic/generic.c
+ * @brief Generic Configuration Function Driver
+ *
+ * This implements a generic composite function
+ *
+ * - idVendor 16 bit word
+ * - idProduct 16 bit word
+ * - bcd_device 16 bit word
+ *
+ * - bDeviceClass byte
+ * - device_sub-class byte
+ * - bcdDevice byte
+ *
+ * - vendor string
+ * - iManufacturer string
+ * - product string
+ * - serial string
+ *
+ * - power byte
+ * - remote_wakeup bit
+ *
+ * - functions string
+ *
+ *
+ * The functions string would contain a list of names, the first would
+ * be the composite function driver, the rest would be the interface
+ * functions. For example:
+ *
+ *
+ * "cmouse-cf mouse-if"
+ * "mcpc-cf dun-if dial-if obex-if dlog-if"
+ *
+ * There are also a set of pre-defined configurations that
+ * can be loaded singly or in toto.
+ *
+ * @ingroup GenericComposite
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-cdc.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-utils.h>
+
+#include "generic.h"
+
+//static otg_tag_t GENERIC;
+
+
+/* USB Module init/exit ***************************************************** */
+
+/*! generic_cf_function_enable - called by USB Device Core to enable the driver
+ * @param function The function instance for this driver to use.
+ * @return non-zero if error.
+ */
+static int generic_cf_function_enable (struct usbd_function_instance *function)
+{
+ //TRACE_MSG0(GENERIC,"--");
+ return 0;
+}
+
+/*! generic_cf_function_disable - called by the USB Device Core to disable the driver
+ * @param function The function instance for this driver
+ */
+/*!@name generic composite functions
+ * @{
+ */
+static void generic_cf_function_disable (struct usbd_function_instance *function)
+{
+ //TRACE_MSG0(GENERIC,"--");
+}
+
+
+/*! function_ops - operations table for the USB Device Core
+ * */
+static struct usbd_function_operations generic_function_ops = {
+ .function_enable = generic_cf_function_enable,
+ .function_disable = generic_cf_function_disable,
+};
+
+
+
+/*! @} */
+
+/*!
+ * generic_register()
+ *
+ */
+void generic_register(struct generic_config *config, const char *match, BOOL override, u16 idVendor, u16 idProduct,
+ char *iSerialNumber, otg_tag_t tag)
+{
+ char *cp, *sp, *lp, *np;
+ const char **interface_list = NULL;
+ int interfaces = 0;
+ char interface_name_list[256];
+
+ TRACE_MSG5(tag, "Driver: \"%s\" idVendor: %04x idProduct: %04x interface_names: \"%s\" match: \"%s\"",
+ config->composite_driver.driver.name, idVendor, idProduct, config->interface_names,
+ match ? match : "");
+
+ printk(KERN_INFO"Driver: \"%s\" idVendor: %04x idProduct: %04x interface_names: \"%s\" match: \"%s\"\n",
+ config->composite_driver.driver.name, idVendor, idProduct, config->interface_names,
+ match ? match : "");
+
+
+ RETURN_IF (match && strlen(match) && strcmp(match, config->composite_driver.driver.name));
+
+ TRACE_MSG2(tag, "loading %s interface_names: %s", match, config->interface_names);
+
+
+ /* decompose interface names to construct interface_list
+ */
+ RETURN_UNLESS (config->interface_names && strlen(config->interface_names));
+
+ /* count interface names and allocate _interface_names array
+ */
+ strncpy(interface_name_list,config->interface_names,sizeof(interface_name_list));
+ interface_name_list[sizeof(interface_name_list)-1]=0;
+ for (cp = sp = interface_name_list, interfaces = 0; cp && *cp; ) {
+ for (; *cp && *cp == ':'; cp++); // skip white space
+ sp = cp; // save start of token
+ for (; *cp && *cp != ':'; cp++); // find end of token
+ BREAK_IF (sp == cp);
+ if (*cp) cp++;
+ interfaces++;
+ }
+
+ THROW_UNLESS(interfaces, error);
+
+ config->interfaces=interfaces;
+ TRACE_MSG1(tag, "interfaces: %d", interfaces);
+
+ THROW_UNLESS((interface_list = (const char **) CKMALLOC (sizeof (char *) * (interfaces + 1))), error);
+
+ for (cp = sp = interface_name_list, interfaces = 0; cp && *cp; interfaces++) {
+ for (; *cp && *cp == ':'; cp++); // skip white space
+ sp = cp; // save start of token
+ for (; *cp && *cp != ':'; cp++); // find end of token
+ BREAK_IF (sp == cp);
+ lp = cp;
+ if (*cp) cp++;
+ np = CKMALLOC(lp - sp + 2);
+ strncpy(np, sp, lp - sp);
+ interface_list[interfaces] = np;
+
+ TRACE_MSG3(tag, "INTERFACE[%2d] %x \"%s\"",
+ interfaces, interface_list[interfaces], interface_list[interfaces]);
+ }
+
+ config->composite_driver.device_description = &config->device_description;
+ config->composite_driver.configuration_description = &config->configuration_description;
+ config->composite_driver.driver.fops = &generic_function_ops;
+ config->interface_list = interface_list;
+
+ if (override) {
+ TRACE_MSG2(tag, "Overide idVendor: %04x idProduct: %04x", idVendor, idProduct);
+ config->composite_driver.device_description->idVendor = idVendor;
+ config->composite_driver.device_description->idProduct = idProduct;
+ }
+
+ if ((iSerialNumber) && strlen(iSerialNumber)){
+ config->composite_driver.device_description->iSerialNumber = iSerialNumber;
+ // config->device_description.iSerialNumber = iSerialNumber;
+ }
+
+ THROW_IF (usbd_register_composite_function (
+ &config->composite_driver,
+ config->composite_driver.driver.name,
+ config->class_name,
+ config->interface_list, NULL), error);
+
+ config->registered++;
+
+ TRACE_MSG0(tag, "REGISTER FINISHED");
+
+ CATCH(error) {
+ TRACE_MSG0(tag, "REGISTER FAILED");
+ // otg_trace_invalidate_tag(GENERIC);
+ }
+
+}
+
+/*!
+ * generic_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the generic driver with the USB Device software.
+ *
+ */
+int generic_modinit (struct generic_config *generic_configs,
+ char *generic_config_name, BOOL override, u16 idVendor, u16 idProduct, char *iSerialNumber, otg_tag_t tag)
+{
+ int i;
+
+
+ TRACE_MSG5(tag, "config_name: \"%s\" Serial: \"%s\" override: %d idVendor: %04x idProduct: %04x",
+ generic_config_name ? generic_config_name : "",
+ iSerialNumber ? iSerialNumber : "",
+ override, idVendor, idProduct);
+
+ /* search for named config
+ */
+ for (i = 0; ; i++) {
+ struct generic_config *config = generic_configs + i;
+ TRACE_MSG2(tag, "i: %d interface_names: %d", i, config->interface_names);
+ BREAK_UNLESS(config->interface_names);
+ generic_register(config, generic_config_name, override, idVendor, idProduct, iSerialNumber, tag);
+ }
+ return 0;
+}
+
+/*!
+ * generic_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ *
+ * @param generic_configs
+ * @param tag
+ * @return void
+ */
+void generic_modexit (struct generic_config *generic_configs, otg_tag_t tag)
+{
+ int i,j;
+ //otg_trace_invalidate_tag(GENERIC);
+ for (i = 0; ; i++) {
+ struct generic_config *config = generic_configs + i;
+
+ /* finished?
+ */
+ BREAK_UNLESS(config->interface_names);
+
+ /* continue if not registered
+ */
+ CONTINUE_UNLESS(config->registered);
+
+ /* de-register this composite function
+ */
+ usbd_deregister_composite_function (&config->composite_driver);
+
+ for (j=0;j<config->interfaces;j++) LKFREE ((char *) config->interface_list[j]);
+
+ if (config->interface_list)
+ LKFREE(config->interface_list);
+ }
+
+ /*
+ if (generic_config.registered)
+ usbd_deregister_composite_function (&generic_config.composite_driver);
+
+ if (generic_config.interface_list)
+ LKFREE(generic_config.interface_list);
+ */
+}
+
+#if OTG_EPILOGUE
+#endif
diff --git a/drivers/otg/functions/generic/generic.h b/drivers/otg/functions/generic/generic.h
new file mode 100644
index 000000000000..4429dcd26876
--- /dev/null
+++ b/drivers/otg/functions/generic/generic.h
@@ -0,0 +1,95 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/generic/generic-cf.h
+ * @(#) tt@belcarra.com|otg/functions/generic/generic.h|20070316204018|00274
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup GenericComposite Generic Composite Function
+ * @ingroup CompositeFunctions
+ */
+
+/*!
+ * @file otg/functions/generic/generic.h
+ * @brief Generic Configuration Function Driver
+ *
+ * This implements a generic composite function
+ *
+ * - idVendor 16 bit word
+ * - idProduct 16 bit word
+ * - bcd_device 16 bit word
+ *
+ * - bDeviceClass byte
+ * - device_sub-class byte
+ * - bcdDevice byte
+ *
+ * - vendor string
+ * - iManufacturer string
+ * - product string
+ * - serial string
+ *
+ * - power byte
+ * - remote_wakeup bit
+ *
+ * - functions string
+ *
+ *
+ * The functions string would contain a list of names, the first would
+ * be the composite function driver, the rest would be the interface
+ * functions. For example:
+ *
+ *
+ * "cmouse-cf mouse-if"
+ * "mcpc-cf dun-if dial-if obex-if dlog-if"
+ *
+ * There are also a set of pre-defined configurations that
+ * can be loaded singly or in toto.
+ *
+ * @ingroup GenericComposite
+ */
+
+
+/*
+ * N.B. This list needs to be synchronized with both the default pre-defined
+ * configuration conditional settings (see above) and the Kconfig list
+ */
+/*! @struct generic_config generic.h "otg/functions/generic/generic.h"
+ */
+struct generic_config {
+ struct usbd_composite_driver composite_driver;
+ const char *class_name;
+ const char *interface_names;
+ const char ** interface_list;
+ struct usbd_configuration_description configuration_description;
+ struct usbd_device_description device_description;
+ int registered;
+ int interfaces;
+};
+
+int generic_modinit (struct generic_config *generic_configs,
+ char *generic_config_name, BOOL override, u16 idVendor, u16 idProduct, char *iSerialNumber,
+ otg_tag_t tag);
+void generic_modexit (struct generic_config *generic_configs,
+ otg_tag_t tag);
+void generic_register(struct generic_config *config, const char *match, BOOL override, u16 idVendor, u16 idProduct,
+ char *iSerialNumber,
+ otg_tag_t tag);
diff --git a/drivers/otg/functions/generic/inteltest-cl.c b/drivers/otg/functions/generic/inteltest-cl.c
new file mode 100644
index 000000000000..1cdcd9e304e0
--- /dev/null
+++ b/drivers/otg/functions/generic/inteltest-cl.c
@@ -0,0 +1,199 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/generic/inteltest-cl.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/generic/inteltest-cl.c|20070425221028|59534
+ *
+ * Copyright (c) 2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @addgroup GenericClass Generic Class Function
+ * @ingroup ClassFunctions
+ */
+
+/*!
+ * @file otg/functions/generic/inteltest-cl.c
+ * @brief Generic Class Function Driver
+ *
+ * This implements a inteltest NULL class function driver.
+ *
+ * @ingroup GenericClass
+ */
+
+#include <otg/otg-compat.h>
+
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+//#include "inteltest.h"
+
+/* Module Parameters ******************************************************** */
+/* !
+ * @name XXXXX MODULE Parameters
+ */
+/* ! @{ */
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Belcarra Generic NULL Class Function");
+
+otg_tag_t GCLASS;
+
+/* ! *} */
+
+/* ********************************************************************************************* */
+/*!
+ * inteltest_cl_device_request - called to process a request to endpoint or interface
+ * @param function
+ * @param request
+ * @return non-zero for failure, will cause endpoint zero stall
+ */
+static int inteltest_cl_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ struct usbd_urb *urb;
+ u16 wLength = le16_to_cpu(request->wLength);
+
+
+ TRACE_MSG5(GCLASS, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x",
+ request->bmRequestType, request->bRequest, request->wValue,
+ request->wIndex, request->wLength);
+
+ /* XXX it should be this, need to verify
+ */
+ RETURN_EINVAL_UNLESS(USB_REQ_RECIPIENT_DEVICE == (request->bmRequestType & USB_REQ_RECIPIENT_MASK));
+
+ switch (ctrl->bRequest) {
+ switch (request->bmRequestType & USB_REQ_DIRECTION_MASK) {
+ case USB_REQ_DEVICE2HOST:
+
+ switch (request->bRequest) {
+ case 0x5c: /* read test */
+ RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0 (function, wLength, NULL)));
+ RETURN_ZERO_UNLESS(rc || usbd_start_in_urb(urb));
+ break;
+ }
+ break;
+
+ case USB_REQ_HOST2DEVICE:
+
+ switch (request->bRequest) {
+ case 0x5b: /* write test */
+ RETURN_EINVAL_UNLESS((urb = usbd_alloc_urb_ep0 (function, wLength, NULL)));
+ RETURN_ZERO_UNLESS(rc || usbd_start_out_urb(urb));
+ break;
+ }
+ break;
+ }
+
+ return -EINVAL;
+}
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations inteltest_function_ops;
+
+/*! inteltest_class_driver - USB Device Core function driver definition
+ */
+struct usbd_class_driver inteltest_class_driver;
+
+#else /* defined(OTG_C99) */
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations inteltest_function_ops = {
+ .device_request = inteltest_cl_device_request, /*!< called for each received device request */
+};
+
+/*! inteltest_class_driver - USB Device Core function driver definition
+ */
+struct usbd_class_driver inteltest_class_driver = {
+ .driver.name = "inteltest-class", /*!< driver name */
+ .driver.fops = &inteltest_function_ops, /*!< operations table */
+};
+#endif /* defined(OTG_C99) */
+
+
+/* USB Module init/exit ***************************************************** */
+
+//#if OTG_EPILOGUE
+/*!
+ * inteltest_cl_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ * @return none
+ */
+static void inteltest_cl_modexit (void)
+{
+ usbd_deregister_class_function (&inteltest_class_driver);
+ otg_trace_invalidate_tag(GCLASS);
+}
+
+module_exit (inteltest_cl_modexit);
+//#endif
+
+/*!
+ * inteltest_cl_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the inteltest driver with the USB Device software.
+ *
+ */
+static int inteltest_cl_modinit (void)
+{
+ #if !defined(OTG_C99)
+ /*! function_ops - operations table for the USB Device Core
+ */
+ ZERO(inteltest_function_ops);
+ inteltest_function_ops.device_request=inteltest_cl_device_request; /*! called for each received device request */
+
+ /*! class_driver - USB Device Core function driver definition
+ */
+ ZERO(inteltest_class_driver);
+ inteltest_class_driver.driver.name = "inteltest-class"; /*! driver name */
+ inteltest_class_driver.driver.fops = &inteltest_function_ops; /*! operations table */
+ #endif /* defined(OTG_C99) */
+
+ GCLASS = otg_trace_obtain_tag(NULL, "inteltest-cf");
+
+ // register as usb function driver
+ TRACE_MSG0(GCLASS, "REGISTER CLASS");
+ THROW_IF (usbd_register_class_function (&inteltest_class_driver, "inteltest-class", NULL), error);
+
+ TRACE_MSG0(GCLASS, "REGISTER FINISHED");
+
+ CATCH(error) {
+ inteltest_cl_modexit();
+ return -EINVAL;
+ }
+ return 0;
+}
+
+module_init (inteltest_cl_modinit);
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/generic/otg-config.h b/drivers/otg/functions/generic/otg-config.h
new file mode 100644
index 000000000000..4b293c7d555c
--- /dev/null
+++ b/drivers/otg/functions/generic/otg-config.h
@@ -0,0 +1,133 @@
+/*
+ * 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
+ */
+/*
+ *
+ * @(#) balden@belcarra.com|otg/functions/generic/otg-config.h|20060419204257|50674
+ *
+ */
+
+/*
+ * tristate " Generic Function"
+ * This acts as a generic function, allowing selection of class
+ * and interface function drivers to create a specific configuration.
+ */
+#define CONFIG_OTG_GENERIC
+
+/*
+ * hex "VendorID (hex value)"
+ * default "0x15ec"
+ */
+#define CONFIG_OTG_GENERIC_VENDORID
+
+/*
+ * hex "ProductID (hex value)"
+ * default "0xf010"
+ */
+#define CONFIG_OTG_GENERIC_PRODUCTID
+
+/*
+ * hex "bcdDevice (binary-coded decimal)"
+ * default "0x0100"
+ */
+#define CONFIG_OTG_GENERIC_BCDDEVICE
+
+/*
+ * string "iManufacturer (string)"
+ * default "Belcarra"
+ */
+#define CONFIG_OTG_GENERIC_MANUFACTURER
+
+/*
+ * string "iProduct (string)"
+ * default "Generic Composite"
+ */
+#define CONFIG_OTG_GENERIC_PRODUCT_NAME
+
+/*
+ * bool 'none'
+ * No pre-defined selection, use the OTG_GENERIC_CONFIG_NAME instead.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_NONE
+
+/*
+ * bool 'mouse'
+ * Configure the mouse driver in a single function non-composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MOUSE
+
+/*
+ * bool 'net-blan'
+ * Configure the network driver in a single function non-composite BLAN configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_NET_BLAN
+
+/*
+ * bool 'net-cdc'
+ * Configure the network driver in a single function non-composite CDC configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_NET_CDC
+
+/*
+ * bool 'net-safe'
+ * Configure the network driver in a single function non-composite SAFE configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_NET_SAFE
+
+/*
+ * bool 'acm-tty'
+ * Configure the acm driver in a single function non-composite TTY configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_ACM_TTY
+
+/*
+ * bool 'msc'
+ * Configure the msc driver in a single function non-composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MSC
+
+/*
+ * bool 'mouse2'
+ * Configure the mouse driver in a demostration, two function composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MOUSE2
+
+/*
+ * bool 'msc-mouse'
+ * Configure the msc and mouse driver in a demostration, two function composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MSC_MOUSE
+
+/*
+ * bool 'msc-blan'
+ * Configure the msc and network-blan driver in a demostration, two function composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MSC_BLAN
+
+/*
+ * bool 'msc-cdc'
+ * Configure the msc and network-cdc driver in a demostration, two function composite configuration.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_MSC_CDC
+
+
+
+
+/*
+ * string "Composite Configuration (string)"
+ * default ""
+ * Name of predefined configuration to be enabled, note that
+ * if this is not defined (empty string) then the first available
+ * configuration will be used (assuming that the required interface
+ * function drivers are available.
+ */
+#define CONFIG_OTG_GENERIC_CONFIG_NAME
diff --git a/drivers/otg/functions/mouse/Kconfig b/drivers/otg/functions/mouse/Kconfig
new file mode 100644
index 000000000000..14e27029ee18
--- /dev/null
+++ b/drivers/otg/functions/mouse/Kconfig
@@ -0,0 +1,141 @@
+# @(#) sp@belcarra.com|otg/functions/mouse/Kconfig|20070417200305|04741
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+menu "OTG Random Mouse function"
+ depends on OTG
+
+config OTG_MOUSE
+ tristate "Random Mouse Function"
+ depends on OTG
+ ---help---
+ This implements a simple Mouse driver that sends HID packets with
+ small random movements. This is a good function for initial testing
+ of Peripheral Controller Drivers as it provides for a complicated
+ enumeration but a simple uni-directional (send Interrupt only) data
+ transport.
+
+menu "Mouse support modules:"
+ depends on OTG_MOUSE && OTG
+
+
+config OTG_MOUSE_INTERFACE
+ bool "Build an interface module"
+ depends on OTG && OTG_MOUSE
+ default "y"
+ ---help---
+ This module needs a composite function module, such as
+ mouse_cf or generic_cf to be activated
+
+config OTG_MOUSE_COMPOSITE
+ bool "Build a simple OTG 2.x style composite function with mouse"
+ depends on OTG && OTG_MOUSE && OTG_MOUSE_INTERFACE
+ default "n"
+ ---help---
+ This module provides a very simple composite function to drive
+ the mouse_if module (see above). The generic_cf module provides
+ a much richer composite function supporting multiple functions.
+
+config OTG_MOUSE_TRADITIONAL
+ bool "Build an OTG 1.x style function module/driver"
+ depends on OTG && OTG_MOUSE
+ default "n"
+ ---help---
+ This is the mouse function driver from previous releases. It
+ does not use the composite/interface mechanism. If this module
+ is used, neither of the other two mouse modules is required.
+
+endmenu
+
+
+menu "OTG Random Mouse function options"
+ depends on OTG && OTG_MOUSE
+
+config OTG_MOUSE_VENDORID
+ hex "VendorID (hex value)"
+ depends on OTG && OTG_MOUSE
+ default "0x15ec"
+ ---help---
+ This parameter is not used by the mouse_if module because the
+ composite layer is responsible for setting this parameter
+
+config OTG_MOUSE_PRODUCTID
+ hex "ProductID (hex value)"
+ depends on OTG && OTG_MOUSE
+ default "0xf003"
+ ---help---
+ Parameters such as PRODUCTID, VENDORID are now the responsibility
+ of the composite driver rather than the interface driver. For this
+ reason, the module parameters vendor_id, etc are not available in
+ the interface versions. Therefore, appropriate values should be set
+ in the kernel configuration. These configuration parameters
+ are referenced in both mouse_cf and generic_cf.
+
+config OTG_MOUSE_BCDDEVICE
+ hex "bcdDevice (binary-coded decimal)"
+ depends on OTG && OTG_MOUSE
+ default "0x0100"
+
+config OTG_MOUSE_MANUFACTURER
+ string "iManufacturer (string)"
+ depends on OTG && OTG_MOUSE
+ default "Belcarra"
+
+config OTG_MOUSE_PRODUCT_NAME
+ string "iProduct (string)"
+ depends on OTG && OTG_MOUSE
+ default "Random Mouse Device"
+
+config OTG_MOUSE_COMM_INTF
+ string "MOUSE Bulk Only iInterface (string)"
+ depends on OTG && OTG_MOUSE
+ default "MOUSE Data Intf"
+
+config OTG_MOUSE_DESC
+ string "Data Interface iConfiguration (string)"
+ depends on OTG && OTG_MOUSE
+ default "MOUSE Configuration"
+
+config OTG_MOUSE_BH
+ bool "MOUSE BH Test"
+ depends on OTG && OTG_MOUSE
+ default n
+ ---help---
+ Implement the Mouse send packet in a bottom half handler,
+ this will delay responses to test the PCD works correctly
+ when the host polls before a send data urb is queued.
+
+config OTG_MOUSE_PACKETS
+ int "Number of packets (zero is continous)"
+ depends on OTG && OTG_MOUSE
+ default 10
+ ---help---
+ Number of Mouse packets to send, will run
+ forever if set to zero.
+
+config OTG_MOUSE_STALL
+ bool "MOUSE Stall Test"
+ depends on OTG && OTG_MOUSE
+ default n
+ ---help---
+ Periodically stall the INTERRUPT endpoint.
+ Used for testing.
+
+config OTG_MOUSE_STALL_COUNT
+ int "Number of Stalls"
+ depends on OTG && OTG_MOUSE_STALL
+ default 1
+ ---help---
+ Number of Stall tests to perform.
+
+config OTG_MOUSE_INTERVAL
+ int 'Polling Interval '
+ depends on OTG && OTG_MOUSE
+ default 1
+ ---help---
+ Sets interrupt endpoint interval.
+
+
+endmenu
+
+
+endmenu
diff --git a/drivers/otg/functions/mouse/Makefile b/drivers/otg/functions/mouse/Makefile
new file mode 100644
index 000000000000..3bd66b674974
--- /dev/null
+++ b/drivers/otg/functions/mouse/Makefile
@@ -0,0 +1,26 @@
+# Function driver for a Random Mouse
+# @(#) balden@belcarra.com|otg/functions/mouse/Makefile-l26|20060419204257|16121
+#
+# Copyright (c) 2004 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+mouse_fd-objs := mouse-fd.o
+mouse_cf-objs := mouse-cf.o
+mouse_if-objs := mouse-if.o
+
+ifeq ($(CONFIG_OTG_MOUSE_TRADITIONAL),y)
+obj-$(CONFIG_OTG_MOUSE) += mouse_fd.o
+endif
+ifeq ($(CONFIG_OTG_MOUSE_INTERFACE), y)
+obj-$(CONFIG_OTG_MOUSE) += mouse_if.o
+endif
+ifeq ($(CONFIG_OTG_MOUSE_COMPOSITE), y)
+obj-$(CONFIG_OTG_MOUSE) += mouse_cf.o
+endif
+
+OTG=$(TOPDIR)/drivers/otg
+MOUSED=$(OTG)/functions/mouse
+OTGCORE_DIR=$(OTG)/otgcore
+#USBDCORE_DIR=$(OTG)/usbdcore
+EXTRA_CFLAGS += -I$(MOUSED) -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
+EXTRA_CFLAGS_nostdinc += -I$(MOUSED) -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
diff --git a/drivers/otg/functions/mouse/mouse-cf.c b/drivers/otg/functions/mouse/mouse-cf.c
new file mode 100644
index 000000000000..31093c667f7b
--- /dev/null
+++ b/drivers/otg/functions/mouse/mouse-cf.c
@@ -0,0 +1,474 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/mouse/mouse-cf.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/mouse/mouse-cf.c|20061218212925|53990
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup MouseComposite Mouse Composite Function
+ * @ingroup CompositeFunctions
+ */
+
+/*!
+ * @file otg/functions/mouse/mouse-cf.c
+ * @brief Mouse Configuration Function Driver
+ *
+ * This implements the composite function portion of the composite
+ * random mouse driver.
+ *
+ * It defines the device descriptor charateristics, selects the
+ * single class function and a list of one or more interface functions.
+ *
+ * The primary purpose of this driver is to demonstrate how to
+ * implement a simple composite function driver and to provide a simple
+ * uni-directional driver for testing USB Device peripheral drivers.
+ *
+ * The mouse driver has several other characteristics to allow testing of
+ * other features of USB Device peripheral drivers:
+ *
+ * - ep0 ZLP handling
+ *
+ * To verify that ep0 ZLP processing is being handling correctly this
+ * driver has a hard coded Product name that is returned as a 32 byte
+ * string. This forces most any implementations that have an endpoint
+ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to
+ * terminate the string transfer when the Product name is requested
+ * by the host.
+ *
+ * There is no request or data handling required for mouse. So this
+ * only implements required descriptors.
+ *
+ * @ingroup MouseComposite
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+/*
+ * ep0 testing.... ensure that this is exactly 16 bytes
+ */
+#undef CONFIG_OTG_MOUSE_PRODUCT_NAME
+#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse"
+
+static const char *mouse_usb_strings[4] = {
+ "Mouse-cf String Test 1",
+ "Mouse-cf String Test 3",
+ "Mouse-cf String Test 5",
+ NULL,
+};
+
+static u8 mouse_usb_string_indexes[4] = {
+ 1, 3, 5, 0,
+};
+
+
+#if !defined(OTG_C99)
+
+/*! @name mouse descriptors
+ * @{ */
+
+/*! Configuration description(s)
+ */
+static struct usbd_configuration_descriptor mouse_configuration_descriptor;
+
+/*! Mouse Configuration Description
+ */
+struct usbd_configuration_description mouse_cf_configuration_description[1];
+
+/*! Device Description
+ */
+static struct usbd_device_descriptor mouse_cf_device_descriptor;
+
+#ifdef CONFIG_OTG_HIGH_SPEED
+/*! High Speed Device Description
+ */
+static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+
+/*! OTG Descriptor
+ */
+static struct usbd_otg_descriptor mouse_otg_descriptor;
+
+/*! Device Description
+ */
+struct usbd_device_description mouse_cf_device_description;
+/* @} */
+
+/*! mouse_cf_glocal_init - initialize global mouse configuration instance
+ */
+
+void mouse_cf_global_init(void)
+{
+ /*! Configuration description(s)
+ */
+ ZERO(mouse_configuration_descriptor);
+ mouse_configuration_descriptor.bLength = 0x09;
+ mouse_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION;
+ mouse_configuration_descriptor.wTotalLength = 0x00;
+
+ /*! Mouse Configuration Description
+ */
+ ZERO(mouse_cf_configuration_description);
+ //mouse_cf_configuration_description[0].configuration_descriptor = &mouse_configuration_descriptor;
+ mouse_cf_configuration_description[0].iConfiguration = "USB Random Mouse Configuration";
+
+ #if 0
+ /*! Device Description
+ */
+ ZERO(mouse_cf_device_descriptor);
+ mouse_cf_device_descriptor.bLength = sizeof(struct usbd_device_descriptor);
+ mouse_cf_device_descriptor.bDescriptorType = USB_DT_DEVICE;
+ mouse_cf_device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
+ mouse_cf_device_descriptor.bDeviceClass = 0x00;
+ mouse_cf_device_descriptor.bDeviceSubClass = 0x00;
+ mouse_cf_device_descriptor.bDeviceProtocol = 0x00;
+ mouse_cf_device_descriptor.bMaxPacketSize0 = 0x00;
+ mouse_cf_device_descriptor.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
+ mouse_cf_device_descriptor.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
+ mouse_cf_device_descriptor.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
+
+#ifdef CONFIG_OTG_HIGH_SPEED
+ /*! High Speed Device Description
+ */
+ ZERO(mouse_device_qualifier_descriptor);
+ mouse_device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor);
+ mouse_device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ mouse_device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
+ mouse_device_qualifier_descriptor.bDeviceClass = 0x00;
+ mouse_device_qualifier_descriptor.bDeviceSubClass = 0x00;
+ mouse_device_qualifier_descriptor.bDeviceProtocol = 0x00;
+ mouse_device_qualifier_descriptor.bMaxPacketSize0 = 0x00;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ #endif
+
+ /*! OTG Descriptor
+ */
+ ZERO(mouse_otg_descriptor);
+ mouse_otg_descriptor.bLength = sizeof(struct usbd_otg_descriptor);
+ mouse_otg_descriptor.bDescriptorType = USB_DT_OTG;
+ mouse_otg_descriptor.bmAttributes = 0;
+
+ /*! Device Description
+ */
+ ZERO(mouse_cf_device_description);
+ //mouse_cf_device_description.device_descriptor = &mouse_cf_device_descriptor;
+#ifdef CONFIG_OTG_HIGH_SPEED
+ mouse_cf_device_description.device_qualifier_descriptor = &mouse_device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ mouse_cf_device_description.bDeviceClass = 0x00;
+ mouse_cf_device_description.bDeviceSubClass = 0x00;
+ mouse_cf_device_description.bDeviceProtocol = 0x00;
+ mouse_cf_device_description.bMaxPacketSize0 = 0x00;
+ mouse_cf_device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
+ mouse_cf_device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
+ mouse_cf_device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
+ mouse_cf_device_description.otg_descriptor = &mouse_otg_descriptor;
+ mouse_cf_device_description.iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER;
+ mouse_cf_device_description.iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME;
+#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
+ mouse_cf_device_description.iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR;
+#endif
+}
+
+#else /* defined(OTG_C99) */
+
+
+/*! @var struct usbd_configuartion_descriptor mouse_configuration_description(s)
+ */
+static struct usbd_configuration_descriptor mouse_configuration_descriptor = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+};
+
+/*! @var struct usbd_configuration_description mouse _configuration_description
+ */
+struct usbd_configuration_description mouse_cf_configuration_description[] = {
+ { //.configuration_descriptor = &mouse_configuration_descriptor,
+ .iConfiguration = "USB Random Mouse Configuration",
+ },
+};
+
+#if 0
+/*! Device Description
+ */
+static struct usbd_device_descriptor mouse_cf_device_descriptor = {
+ .bLength = sizeof(struct usbd_device_descriptor),
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION),
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 0x00,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE),
+};
+
+#ifdef CONFIG_OTG_HIGH_SPEED
+/*! High Speed Device Description
+ */
+static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor = {
+ .bLength = sizeof(struct usbd_device_qualifier_descriptor),
+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+ .bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION),
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 0x00,
+};
+#endif /* CONFIG_OTG_HIGH_SPEED */
+#endif
+
+#if 0
+/*! OTG Descriptor
+ */
+static struct usbd_otg_descriptor mouse_otg_descriptor = {
+ .bLength = sizeof(struct usbd_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = 0,
+};
+#endif
+
+/*! @var struct usbd_device_description mouse_cf_device_description
+ */
+struct usbd_device_description mouse_cf_device_description = {
+ //.device_descriptor = &mouse_cf_device_descriptor,
+#ifdef CONFIG_OTG_HIGH_SPEED
+ .device_qualifier_descriptor = &mouse_device_qualifier_descriptor,
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 0x00,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE),
+ //.otg_descriptor = &mouse_otg_descriptor,
+ .iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER,
+ .iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME,
+#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
+ .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR,
+#endif
+};
+#endif /* defined(OTG_C99) */
+
+
+/*! @} */
+
+
+/* MOUSE ***************************************************************************************** */
+
+/* USB Device Functions ************************************************************************ */
+
+/*! mouse_cf_function_enable - called by USB Device Core to enable the driver
+ * @param function The function instance for this driver to use.
+ * @return non-zero if error.
+ */
+static int mouse_cf_function_enable (struct usbd_function_instance *function)
+{
+ // XXX MODULE LOCK HERE
+ return 0;
+}
+
+/*! mouse_cf_function_disable - called by the USB Device Core to disable the driver
+ * @param function The function instance for this driver
+ */
+static void mouse_cf_function_disable (struct usbd_function_instance *function)
+{
+ // XXX MODULE UNLOCK HERE
+}
+
+
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+/*! struct usbd_functions mouse_function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations mouse_function_ops;
+
+/*! struct usbd_composite_driver mouse_composite_driver - USB Device Core function driver definition
+ */
+struct usbd_composite_driver mouse_composite_driver;
+
+/*! mouse_cf_ops_init - initialze operations table for mouse configuration called by usbd core
+ */
+
+void mouse_cf_ops_init(void)
+{
+ /*! function_ops - operations table for the USB Device Core
+ */
+ ZERO(mouse_function_ops);
+ mouse_function_ops.function_enable = mouse_cf_function_enable;
+ mouse_function_ops.function_disable = mouse_cf_function_disable;
+
+ /*! composite_driver - USB Device Core function driver definition
+ */
+ ZERO(mouse_composite_driver);
+ mouse_composite_driver.driver.name = "mouse-random-fd"; /*! driver name */
+ mouse_composite_driver.driver.fops = mouse_function_ops; /*! operations table */
+ mouse_composite_driver.driver.usb_strings = mouse_usb_strings; /*! operations table */
+ mouse_composite_driver.driver.usb_string_indexes = &mouse_usb_string_indexes; /*! operations table */
+ mouse_composite_driver.device_description = &mouse_cf_device_description; /*! mouse device description */
+ mouse_composite_driver.bNumConfigurations =
+ sizeof (mouse_cf_configuration_description) / sizeof (struct usbd_configuration_description);
+ mouse_composite_driver.configuration_description= mouse_cf_configuration_description; /*! mouse configuration */
+ //mouse_composite_driver.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
+ //mouse_composite_driver.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
+ //mouse_composite_driver.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
+
+ //mouse_composite_driver.interfaces = 0;
+ //mouse_composite_driver.interface_list = NULL;
+ //mouse_composite_driver.endpointsRequested = 0;
+ //mouse_composite_driver.requestedEndpoints = NULL;
+}
+#else /* defined(OTG_C99) */
+/*! @var struct usbd_function_operation mouse_function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations mouse_function_ops = {
+ .function_enable = mouse_cf_function_enable,
+ .function_disable = mouse_cf_function_disable,
+};
+
+/*! mouse_composite_driver - USB Device Core function driver definition
+ */
+struct usbd_composite_driver mouse_composite_driver = {
+ .driver = {
+ .name = "mouse-random-cf",
+ .fops = &mouse_function_ops,
+ .usb_strings = mouse_usb_strings,
+ .usb_string_indexes = mouse_usb_string_indexes,
+ },
+ .device_description = &mouse_cf_device_description,
+ .bNumConfigurations = sizeof (mouse_cf_configuration_description) / sizeof (struct usbd_configuration_description),
+ .configuration_description = mouse_cf_configuration_description,
+};
+#endif /* defined(OTG_C99) */
+
+
+/* Module Parameters ******************************************************** */
+/* !
+ * @name XXXXX MODULE Parameters
+ */
+/* ! @{ */
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
+
+static u32 vendor_id; /*!< override built-in vendor ID */
+static u32 product_id; /*!< override built-in product ID */
+
+MOD_PARM (vendor_id, "i");
+MOD_PARM (product_id, "i");
+
+MOD_PARM_DESC (vendor_id, "Device Vendor ID");
+MOD_PARM_DESC (product_id, "Device Product ID");
+
+otg_tag_t MOUSE;
+/* ! *} */
+
+/* USB Module init/exit ***************************************************** */
+/*! char *mouse_arg_list[] */
+char *mouse_arg_list[] = {
+ //"2=mouse-random-if",
+ "mouse-random-if",
+ //"mouse-random-if",
+ //"mouse-random-if",
+ //"mouse-random-if",
+ NULL,
+};
+
+/*!
+ * mouse_cf_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the mouse driver with the USB Device software.
+ *
+ * @return 0 on success
+ *
+ */
+static int mouse_cf_modinit (void)
+{
+ int i;
+ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id);
+
+ #if !defined(OTG_C99)
+ mouse_cf_global_init();
+ mouse_cf_ops_init();
+ #endif /* defined(OTG_C99) */
+
+ MOUSE = otg_trace_obtain_tag(NULL, "mouse-cf");
+ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id);
+
+ //if (vendor_id)
+ // mouse_composite_driver.idVendor = cpu_to_le16(vendor_id);
+ //if (product_id)
+ // mouse_composite_driver.idProduct = cpu_to_le16(product_id);
+
+
+ // register as usb function driver
+ TRACE_MSG0(MOUSE, "REGISTER COMPOSITE");
+
+ THROW_IF (usbd_register_composite_function (&mouse_composite_driver,
+ "mouse-random-cf", NULL, mouse_arg_list, NULL), error);
+
+ TRACE_MSG0(MOUSE, "REGISTER FINISHED");
+
+ CATCH(error) {
+ otg_trace_invalidate_tag(MOUSE);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+module_init (mouse_cf_modinit);
+
+#if OTG_EPILOGUE
+/*!
+ * mouse_cf_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ *
+ * @param void
+ * @return void
+ */
+static void mouse_cf_modexit (void)
+{
+ usbd_deregister_composite_function (&mouse_composite_driver);
+ otg_trace_invalidate_tag(MOUSE);
+}
+
+module_exit (mouse_cf_modexit);
+#endif
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/mouse/mouse-fd.c b/drivers/otg/functions/mouse/mouse-fd.c
new file mode 100644
index 000000000000..617aae1fac72
--- /dev/null
+++ b/drivers/otg/functions/mouse/mouse-fd.c
@@ -0,0 +1,901 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/mouse/mouse-fd.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/mouse/mouse-fd.c|20061218212925|31740
+ *
+ * Copyright (c) 2003-2004 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup MouseSimple Mouse Simple Function
+ * @ingroup SimpleFunctions
+ */
+
+/*!
+ * @file otg/functions/mouse/mouse-fd.c
+ * @brief Mouse Function Driver protocol implementation.
+ *
+ * This file implements the Mouse HID protocols and will generate
+ * random mouse data on the INTERRUPT endpoint.
+ *
+ * The primary purpose of this driver is to demonstrate how to
+ * implement a simple function driver and to provide a simple
+ * uni-directional driver for testing USB Device peripheral drivers.
+ *
+ * The mouse driver has several other characteristics to allow testing of
+ * other features of USB Device peripheral drivers:
+ *
+ * - ep0 ZLP handling
+ *
+ * - ep0 delayed CONTROL READ
+ *
+ * To verify that ep0 ZLP processing is being handling correctly this
+ * driver has a hard coded Product name that is returned as a 32 byte
+ * string. This forces most any implementations that have an endpoint
+ * zero packetsize of 8, 16 or 32 to send a Zero Length Packet to
+ * terminate the string transfer when the Product name is requested
+ * by the host.
+ *
+ * The delayed CONTROL READ test verifies that that USB Device peripheral
+ * drivers will correctly NAK until data is provide for device requests.
+ * This is done by (optionally) delaying the HID report via a bottom half
+ * handler. The device request returns normally and the peripheral driver
+ * must properly recognize that while the device request had a non-zero
+ * wLength field, there is currently no queued urb.
+ *
+ * When the bottom half handler is scheduled the HID report urb will be
+ * queued on endpoint zero and then returned to the host.
+ *
+ *
+ * @ingroup MouseSimple
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-hid.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+/*!@struct mouse_private mouse-fd.c otg/functions/mouse/mouse-fd.c
+ * @brief The mouse_private structure is used to collect all of the mouse driver
+ * global variables into one place.
+ */
+struct mouse_private {
+#ifdef CONFIG_OTG_MOUSE_BH
+ struct WORK_STRUCT notification_bh;
+#endif /* CONFIG_OTG_MOUSE_BH */
+ int usb_driver_registered; /*!< non-zero if usb function registered */
+ unsigned char connected; /*!< non-zero if connected to host (configured) */
+ unsigned int writesize; /*!< packetsize * 4 */
+ struct usbd_urb *tx_urb; /*!< saved copy of current tx urb */
+ int wLength;
+ int x;
+ int y;
+ int last_x;
+ int last_y;
+ int n;
+};
+
+#ifndef OTG_C99
+extern void mouse_global_init(void);
+#endif /* OTG_C99 */
+
+#define MOUSE mouse_fd_trace_tag
+extern otg_tag_t MOUSE;
+
+struct mouse_private mouse_private;
+
+/*
+ * ep0 testing.... ensure that this is exactly 16 bytes
+ */
+#undef CONFIG_OTG_MOUSE_PRODUCT_NAME
+#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Belcarra Mouse"
+
+/*!
+ * @name Mouse Descriptors
+ *
+ * MouseHIDReport
+ * MOUSE Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+
+/*! @name Mouse Descriptors
+ */
+/* @{*/
+
+#define BULK_INT 0x00 /*!< Interrupt endpoint number */
+#define ENDPOINTS 0x01 /*!< Number of endpoints required */
+
+/*! List of required endpoint numbers
+ */
+static u8 mouse_index[] = { BULK_INT, };
+
+/*! List of requested endpoints
+ */
+static struct usbd_endpoint_request mouse_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_INT, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, 2, },
+ { 0, },
+};
+
+/*! This is the HID report returned for the HID Device Request.
+ * There is no pre-defined descriptor type for this.
+ */
+static char MouseHIDReport[52] = {
+ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x02, /*!< USAGE (Mouse) */
+ 0xa1, 0x01, /*!< COLLECTION (Application) */
+ 0x09, 0x01, /*!< USAGE (Pointer) */
+ 0xa1, 0x00, /*!< COLLECTION (Physical) */
+ 0x05, 0x09, /*!< USAGE_PAGE (Button) */
+ 0x19, 0x01, /*!< USAGE_MINIMUM (Button 1) */
+ 0x29, 0x03, /*!< USAGE_MAXIMUM (Button 3) */
+ 0x15, 0x00, /*!< LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /*!< LOGICAL_MAXIMUM (1) */
+ 0x95, 0x03, /*!< REPORT_COUNT (3) */
+ 0x75, 0x01, /*!< REPORT_SIZE (1) */
+ 0x81, 0x02, /*!< INPUT (Data,Var,Abs) */
+ 0x95, 0x01, /*!< REPORT_COUNT (1) */
+ 0x75, 0x05, /*!< REPORT_SIZE (5) */
+ 0x81, 0x03, /*!< INPUT (Cnst,Var,Abs) */
+ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /*!< USAGE (X) */
+ 0x09, 0x31, /*!< USAGE (Y) */
+ 0x09, 0x38, /*!< USAGE (WHEEL) */
+ 0x15, 0x81, /*!< LOGICAL_MINIMUM (-127) */
+ 0x25, 0x7f, /*!< LOGICAL_MAXIMUM (127) */
+ 0x75, 0x08, /*!< REPORT_SIZE (8) */
+ 0x95, 0x03, /*!< REPORT_COUNT (3) */
+ 0x81, 0x06, /*!< INPUT (Data,Var,Rel) */
+ 0xc0, /*!< END_COLLECTION */
+ 0xc0 /*!< END_COLLECTION */
+};
+
+#if !defined(OTG_C99)
+
+/*! struct hid_descriptor mouse_fd_hid - This is the HID desccriptor.
+ */
+static struct hid_descriptor mouse_fd_hid;
+
+/*! staruct usbd_geeric_class_descriptor * mouse_fd_hid_descriptor - List of class descriptors
+ */
+static struct usbd_generic_class_descriptor *mouse_fd_hid_descriptors[] = {
+ (struct usbd_generic_class_descriptor *)&mouse_fd_hid, };
+
+/*! struct usbd_interface_descriptor mouse_date_alternate_descriptor - Data Interface Alternate description
+ */
+static struct usbd_interface_descriptor mouse_data_alternate_descriptor;
+
+/*! struct usbd_alternate_description mouse_date_alternate_descriptions - Interface Descriptions
+ */
+static struct usbd_alternate_description mouse_data_alternate_descriptions[1];
+
+/*! struct usbd_interface_description mouse_interfaces - List of Interface description(s)
+ */
+static struct usbd_interface_description mouse_interfaces[1];
+
+/*! struct usbd_configuration_descriptor mouse_configuration_descriptor - Configuration description(s)
+ */
+static struct usbd_configuration_descriptor mouse_configuration_descriptor;
+
+/*! struct usbd_configuration_description mouse_fd_configuration - Mouse Configuration Description
+ */
+struct usbd_configuration_description mouse_fd_configuration_description[1];
+
+/*! struct usbd_device_descriptor mouse_fd_device_descriptor - Device Description
+ */
+static struct usbd_device_descriptor mouse_fd_device_descriptor;
+
+#ifdef CONFIG_OTG_HIGH_SPEED
+/*! struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor - High Speed Device Description
+ */
+static struct usbd_device_qualifier_descriptor mouse_device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+
+/*! struct usbd_otg_descriptor mouse_otg_descriptor - OTG Descriptor
+ */
+static struct usbd_otg_descriptor mouse_otg_descriptor;
+
+/*! Device Description
+ */
+struct usbd_device_description mouse_fd_device_description;
+/* mouse_global_init - initialize global variables for mouse instance
+ */
+void mouse_global_init(void)
+{
+ /*! This is the HID desccriptor.
+ */
+ ZERO(mouse_fd_hid);
+ mouse_fd_hid.bLength = 0x09;
+ mouse_fd_hid.bDescriptorType = 0x21;
+ mouse_fd_hid.bcdHID = __constant_cpu_to_le16(0x110);
+ mouse_fd_hid.bCountryCode = 0x00;
+ mouse_fd_hid.bNumDescriptors = 0x01;
+ mouse_fd_hid.bReportType = 0x22;
+ mouse_fd_hid.wItemLength = __constant_cpu_to_le16(0x34);
+
+
+ /*! Data Interface Alternate description
+ */
+ ZERO(mouse_data_alternate_descriptor);
+ mouse_data_alternate_descriptor.bLength = 0x09;
+ mouse_data_alternate_descriptor.bDescriptorType = USB_DT_INTERFACE;
+ mouse_data_alternate_descriptor.bInterfaceNumber = 0x00;
+ mouse_data_alternate_descriptor.bAlternateSetting = 0x00;
+ mouse_data_alternate_descriptor.bNumEndpoints = 0x01;
+ mouse_data_alternate_descriptor.bInterfaceClass = 0x03;
+ mouse_data_alternate_descriptor.bInterfaceSubClass = 0x01;
+ mouse_data_alternate_descriptor.bInterfaceProtocol = 0x02;
+ mouse_data_alternate_descriptor.iInterface = 0x00;
+
+ /*! Interface Descriptions
+ */
+ ZERO(mouse_data_alternate_descriptions);
+ mouse_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt";
+ //mouse_data_alternate_descriptions[0].interface_descriptor = &mouse_data_alternate_descriptor;
+ mouse_data_alternate_descriptions[0].classes =
+ sizeof (mouse_fd_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *);
+ mouse_data_alternate_descriptions[0].class_list = mouse_fd_hid_descriptors;
+ mouse_data_alternate_descriptions[0].endpoints = 0x01;
+ mouse_data_alternate_descriptions[0].endpoint_index = mouse_index,
+
+ /*! List of Interface description(s)
+ */
+ ZERO(mouse_interfaces);
+ mouse_interfaces[0].alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description);
+ mouse_interfaces[0].alternate_list = mouse_data_alternate_descriptions;
+
+
+ /*! Configuration description(s)
+ */
+ ZERO(mouse_configuration_descriptor);
+ mouse_configuration_descriptor.bLength = 0x09;
+ mouse_configuration_descriptor.bDescriptorType = USB_DT_CONFIGURATION;
+ mouse_configuration_descriptor.wTotalLength = 0x00;
+ mouse_configuration_descriptor.bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
+ mouse_configuration_descriptor.bConfigurationValue = 0x01;
+ mouse_configuration_descriptor.iConfiguration = 0x00;
+ mouse_configuration_descriptor.bmAttributes = 0;
+ mouse_configuration_descriptor.bMaxPower = 0;
+
+ /*! Mouse Configuration Description
+ */
+ ZERO(mouse_fd_configuration_description);
+ //mouse_fd_configuration_description[0].configuration_descriptor = &mouse_configuration_descriptor;
+ mouse_fd_configuration_description[0].iConfiguration = "USB Random Mouse Configuration";
+ //mouse_fd_configuration_description[0].interfaces =
+ // sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
+ // mouse_fd_configuration_description[0].interface_list = mouse_interfaces;
+
+ /*! Device Description
+ */
+ ZERO(mouse_fd_device_description);
+ //mouse_fd_device_description.device_descriptor = &mouse_fd_device_descriptor;
+#ifdef CONFIG_OTG_HIGH_SPEED
+ mouse_fd_device_description.device_qualifier_descriptor = &mouse_device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ mouse_fd_device_description.bDeviceClass = 0x00;
+ mouse_fd_device_description.bDeviceSubClass = 0x00;
+ mouse_fd_device_description.bDeviceProtocol = 0x00;
+ mouse_fd_device_description.bMaxPacketSize0 = 0x00;
+ mouse_fd_device_description.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
+ mouse_fd_device_description.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
+ mouse_fd_device_description.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
+ mouse_fd_device_description.otg_descriptor = &mouse_otg_descriptor;
+ mouse_fd_device_description.iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER;
+ mouse_fd_device_description.iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME;
+#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
+ mouse_fd_device_description.iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR;
+#endif
+}
+
+#else /* defined(OTG_C99) */
+
+/*! struct hid_descriptor mouse_fd_hid - This is the HID desccriptor.
+ */
+struct hid_descriptor mouse_fd_hid = {
+ .bLength = 0x09,
+ .bDescriptorType = 0x21,
+ .bcdHID = __constant_cpu_to_le16(0x110),
+ .bCountryCode = 0x00,
+ .bNumDescriptors = 0x01,
+ .bReportType = 0x22,
+ .wItemLength = __constant_cpu_to_le16(0x34),
+};
+
+/*! struct usbd_generic_class_descriptor mouse_fd_hid_descriptor - List of class descriptors
+ */
+static struct usbd_generic_class_descriptor *mouse_fd_hid_descriptors[] = {
+ (struct usbd_generic_class_descriptor *)&mouse_fd_hid, };
+
+/*! struct usbd_alternate_descriptor mouse_date_alternate_descriptor - Interface Descriptions
+ */
+static struct usbd_alternate_description mouse_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Random Mouse Interface - Interrupt",
+ .bInterfaceClass = 0x03,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x02,
+ .classes = sizeof (mouse_fd_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = mouse_fd_hid_descriptors,
+ .endpoints = sizeof (mouse_index) / sizeof(u8),
+ .endpoint_index = mouse_index,
+ },
+};
+
+/*! struct usbd_interfacd_description mouse_interfaces - List of Interface description(s)
+ */
+static struct usbd_interface_description mouse_interfaces[] = {
+ { .alternates = sizeof (mouse_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = mouse_data_alternate_descriptions,},
+};
+
+
+/*! struct usbd_configuration_descriptor mouse_configuration_descriptor - Configuration description(s)
+ */
+static struct usbd_configuration_descriptor mouse_configuration_descriptor = {
+ .bLength = 0x09,
+ .bDescriptorType = USB_DT_CONFIGURATION,
+ .wTotalLength = 0x00,
+ .bNumInterfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description),
+ .bConfigurationValue = 0x01,
+ .iConfiguration = 0x00,
+ .bmAttributes = 0,
+ .bMaxPower = 0,
+};
+
+/*! struct usbd_configuration_description mouse_fd_configuration_description[] - Mouse Configuration Description
+ */
+struct usbd_configuration_description mouse_fd_configuration_description[] = {
+ { //.configuration_descriptor = &mouse_configuration_descriptor,
+ .iConfiguration = "USB Random Mouse Configuration",
+ },
+};
+
+#if 0
+/*! OTG Descriptor
+ */
+static struct usbd_otg_descriptor mouse_otg_descriptor = {
+ .bLength = sizeof(struct usbd_otg_descriptor),
+ .bDescriptorType = USB_DT_OTG,
+ .bmAttributes = 0,
+};
+#endif
+
+/*! struct usbd_device_description mouse_fd_device_description - Device Description
+ */
+struct usbd_device_description mouse_fd_device_description = {
+ //.device_descriptor = &mouse_fd_device_descriptor,
+#ifdef CONFIG_OTG_HIGH_SPEED
+ .device_qualifier_descriptor = &mouse_device_qualifier_descriptor,
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = 0x00,
+ .idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID),
+ .idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID),
+ .bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE),
+ //.otg_descriptor = &mouse_otg_descriptor,
+ .iManufacturer = CONFIG_OTG_MOUSE_MANUFACTURER,
+ .iProduct = CONFIG_OTG_MOUSE_PRODUCT_NAME,
+#if !defined(CONFIG_OTG_NO_SERIAL_NUMBER) && defined(CONFIG_OTG_SERIAL_NUMBER_STR)
+ .iSerialNumber = CONFIG_OTG_SERIAL_NUMBER_STR,
+#endif
+};
+#endif /* defined(OTG_C99) */
+
+
+/*@}*/
+
+static int mouse_count;
+static int mouse_stalls;
+
+/* MOUSE ***************************************************************************************** */
+/* Transmit Function *************************************************************************** */
+/*! int get_xy[] */
+static int get_xy[8] = {
+ 0, 0, 1, 1,
+ 0, 0, -1, -1,
+};
+
+/*!
+ * mouse_fd_send() - send a mouse data urb with random data
+ * @param function
+ * @return void
+ */
+void mouse_fd_send(struct usbd_function_instance *function)
+{
+ struct mouse_private *mouse = &mouse_private;
+ int new_x = 0;
+ int new_y = 0;
+ u8 random;
+
+ TRACE_MSG1(MOUSE, "mouse_count: %d", mouse_count);
+
+ memset(mouse->tx_urb->buffer, 0, 4);
+ if (!mouse->n) {
+
+ get_random_bytes(&random, 1);
+
+ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7]));
+ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7]));
+ mouse->n = (random>>6) & 0x3;
+
+ new_x = mouse->x + mouse->last_x;
+ new_y = mouse->y + mouse->last_y;
+
+ mouse->tx_urb->buffer[1] = mouse->last_x;
+ mouse->x = new_x;
+ mouse->tx_urb->buffer[2] = mouse->last_y;
+ mouse->y = new_y;
+
+ }
+ else if ((mouse->n)&1) {
+ mouse->n--;
+ }
+ else {
+ mouse->n--;
+ mouse->tx_urb->buffer[1] = mouse->last_x;
+ mouse->x = new_x;
+ mouse->tx_urb->buffer[2] = mouse->last_y;
+ mouse->y = new_y;
+ }
+
+ mouse->tx_urb->actual_length = 4;
+ usbd_start_in_urb(mouse->tx_urb);
+}
+
+/*!
+ * mouse_urb_sent() - called to indicate URB transmit finished
+ * This function is the callback function for sent urbs.
+ * It simply queues up another urb until the packets to be sent
+ * configuration parameter is reached (or forever if zero.)
+ * @param urb The urb to be sent.
+ * @param rc result
+ * @return int Return non-zero for failure.
+ */
+static int mouse_urb_sent (struct usbd_urb *urb, int rc)
+{
+ struct mouse_private *mouse = &mouse_private;
+ struct usbd_function_instance *function = urb->function_instance;
+
+ RETURN_ZERO_IF(usbd_get_device_status(function) == USBD_CLOSING);
+ RETURN_ZERO_IF(usbd_get_device_status(function) != USBD_OK);
+ RETURN_ZERO_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
+
+ TRACE_MSG2(MOUSE, "mouse_count: %d urb status: %d", mouse_count, urb->status);
+
+ #ifndef CONFIG_OTG_MOUSE_STALL
+ #ifdef CONFIG_OTG_MOUSE_PACKETS
+ TRACE_MSG0(MOUSE," Normal routine");
+ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse_count++ > CONFIG_OTG_MOUSE_PACKETS));
+ mouse_fd_send(function); // re-send
+ return 0;
+ #endif /* CONFIG_OTG_MOUSE_PACKETS */
+ #else /* CONFIG_OTG_MOUSE_STALL */
+
+ if (mouse_count++ < 7) {
+ mouse_fd_send(function); // re-send
+ }
+ else {
+ usbd_halt_endpoint(function, BULK_INT);
+ mouse_count = 0;
+ TRACE_MSG0(MOUSE," MOUSE STALL");
+ }
+ #endif /* CONFIG_OTG_MOUSE_STALL */
+ return 0;
+}
+
+/* USB Device Functions ************************************************************************ */
+
+typedef enum mesg {
+ mesg_unknown,
+ mesg_configured,
+ mesg_reset,
+} mesg_t;
+mesg_t mouse_last_mesg;
+
+char * mouse_messages[3] = {
+ "",
+ "Mouse Configured",
+ "Mouse Reset",
+};
+/*! mouse_check_mesg -
+ * @param curr_mesg -
+ */
+void mouse_check_mesg(mesg_t curr_mesg)
+{
+ RETURN_UNLESS(mouse_last_mesg != curr_mesg);
+ mouse_last_mesg = curr_mesg;
+ otg_message(mouse_messages[curr_mesg]);
+}
+
+/*!
+ * mouse_event_handler() - process a device event
+ * This function is called to process USB Device Events.
+ *
+ * The DEVICE_CONFIGURED event causes the mouse_fd_send() to be called
+ * to start the data flow.
+ *
+ * The DEVICE_RESET or DEVICE_DE_CONFIGURED events cause the outstanding
+ * transmit urb to be cancelled.
+ *
+ * @param function the function instance
+ * @param event the event
+ * @param data
+ * @return void
+ *
+ */
+void mouse_event_handler (struct usbd_function_instance *function, usbd_device_event_t event, int data)
+{
+ struct mouse_private *mouse = &mouse_private;
+
+ switch (event) {
+ case DEVICE_CONFIGURED:
+
+ TRACE_MSG0(MOUSE, "Mouse Configured");
+ mouse_check_mesg(mesg_configured);
+ mouse_count = 0;
+ mouse->connected = 1;
+ if (!(mouse->tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent)))
+ printk(KERN_INFO"%s: alloc failed\n", __FUNCTION__);
+ mouse_fd_send(function); // start sending
+ break;
+
+ case DEVICE_RESET:
+ TRACE_MSG0(MOUSE, "Mouse Reset");
+ case DEVICE_DE_CONFIGURED:
+ TRACE_MSG0(MOUSE, "Mouse De-Configured");
+ mouse_check_mesg(mesg_reset);
+ BREAK_IF(!mouse->connected);
+ mouse->connected = 0;
+ if (mouse->tx_urb) {
+ usbd_free_urb (mouse->tx_urb);
+ mouse->tx_urb = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*! copy_report()
+ * This function copies the Mouse HID report into the provided URB.
+ * @param urb Destination
+ * @param data Source
+ * @param size Amount of data to copy.
+ * @param max_buf Size of buffer
+ * @return Non-zero if error.
+ *
+ */
+static int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf)
+{
+ int available;
+ int length;
+
+ RETURN_EINVAL_IF (!urb);
+ RETURN_EINVAL_IF (!data);
+ RETURN_EINVAL_IF (!(length = size));
+ RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0);
+
+ length = (length < available) ? length : available;
+ memcpy (urb->buffer + urb->actual_length, data, length);
+ urb->actual_length += length;
+ return 0;
+}
+
+/*!
+ * mouse_fd_send_hid() - send an EP0 urb containing HID report
+ * This is called to send the Mouse HID report.
+ */
+static int mouse_fd_send_hid (struct usbd_function_instance *function)
+{
+ struct mouse_private *mouse = &mouse_private;
+ struct usbd_urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL);
+ int wMaxPacketSize = usbd_endpoint_zero_wMaxPacketSize(urb->function_instance, 0);
+
+ TRACE_MSG1(MOUSE, "Send Hid wLength: %d", mouse->wLength);
+ RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength));
+ RETURN_EINVAL_UNLESS (wMaxPacketSize);
+
+ if (!(urb->actual_length % wMaxPacketSize) && (urb->actual_length < mouse->wLength))
+ urb->flags |= USBD_URB_SENDZLP;
+
+ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
+ usbd_free_urb(urb);
+ return -EINVAL;
+}
+
+#ifdef CONFIG_OTG_MOUSE_BH
+/*!
+ * mouse_fd_hid_bh() - Bottom half handler to send a HID report
+ * @param data
+ */
+static void mouse_fd_hid_bh (void *data)
+{
+ struct usbd_function_instance *function = mouse_private.function;
+ mouse_fd_send_hid(function);
+}
+
+/*! mouse_schedule_bh - schedule a call for mouse_fd_hid_bh
+ * @param void
+ */
+static int mouse_schedule_bh (struct usbd_function_instance *function)
+{
+ TRACE_MSG0(MOUSE, "Scheduling");
+ SET_WORK_ARG(&mouse_private.notification_bh, (void)function)
+ return (!schedule_task (&mouse_private.notification_bh)) ? EINVAL : 0;
+}
+#endif /* CONFIG_OTG_MOUSE_BH */
+
+/*!
+ * mouse_device_request - called to indicate urb has been received
+ * @param function
+ * @param request
+ */
+int mouse_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ /* verify that this is a usb class request per cdc-mouse specification or a vendor request.
+ * determine the request direction and process accordingly
+ */
+
+ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+ case USB_REQ_HOST2DEVICE:
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:
+ return 0;
+
+ case USB_REQ_DEVICE2HOST :
+ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
+ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_VENDOR:
+
+ switch (request->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ switch (le16_to_cpu(request->wValue)>>8) {
+ case HID_REPORT:
+ mouse_private.wLength = request->wLength;
+ #ifdef CONFIG_OTG_MOUSE_BH
+ return mouse_schedule_bh(function);
+ #else
+ return mouse_fd_send_hid(function);
+ #endif
+ }
+ default: break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+
+/*! mouse_function_enable - called by USB Device Core to enable the driver
+ * @param function The function instance for this driver to use.
+ * @return non-zero if error.
+ */
+static int mouse_function_enable (struct usbd_function_instance *function)
+{
+ struct mouse_private *mouse = &mouse_private;
+
+ // XXX MODULE LOCK HERE
+ mouse->n = 0;
+ mouse->x = 0;
+ mouse->y = 0;
+ mouse->last_x = 0;
+ mouse->last_y = 0;
+ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, 0);
+ return 0;
+}
+
+/*! mouse_function_disable - called by the USB Device Core to disable the driver
+ * @param function The function instance for this driver
+ */
+static void mouse_function_disable (struct usbd_function_instance *function)
+{
+ struct mouse_private *mouse = &mouse_private;
+ mouse->writesize = 0;
+ // XXX MODULE UNLOCK HERE
+}
+
+static void mouse_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress)
+{
+
+ TRACE_MSG1(MOUSE,"CLEARED bEndpointAddress: %02x", bEndpointAddress);
+
+}
+
+
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations mouse_function_ops;
+
+/*! mouse_simple_driver - USB Device Core function driver definition
+ */
+struct usbd_simple_driver mouse_simple_driver;
+/*! mouse_ops_init - initialize mouse ops
+ */
+void mouse_ops_init(void)
+{
+ /*! function_ops - operations table for the USB Device Core
+ */
+ ZERO(mouse_function_ops);
+ mouse_function_ops.event_handler = mouse_event_handler; /*! called for each USB Device Event */
+ mouse_function_ops.device_request = mouse_device_request; /*! called for each received device request */
+ mouse_function_ops.function_enable = mouse_function_enable; /*! called to enable the function driver */
+ mouse_function_ops.function_disable = mouse_function_disable; /*! called to disable the function driver */
+ mouse_function_ops.endpoint_cleared = mouse_endpoint_cleared;
+
+ /*! simple_driver - USB Device Core function driver definition
+ */
+ ZERO(mouse_simple_driver);
+ mouse_simple_driver.driver.name = "mouse-random"; /*! driver name */
+ mouse_simple_driver.driver.fops = &mouse_function_ops; /*! operations table */
+ mouse_simple_driver.device_description = &mouse_fd_device_description; /*! mouse device description */
+ mouse_simple_driver.bNumConfigurations =
+ sizeof (mouse_fd_configuration_description) / sizeof (struct usbd_configuration_description);
+ mouse_simple_driver.configuration_description =
+ mouse_fd_configuration_description; /*! mouse configuration description */
+ //mouse_simple_driver.idVendor = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_VENDORID);
+ //mouse_simple_driver.idProduct = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_PRODUCTID);
+ //mouse_simple_driver.bcdDevice = __constant_cpu_to_le16(CONFIG_OTG_MOUSE_BCDDEVICE);
+
+
+ mouse_simple_driver.interfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
+ mouse_simple_driver.interface_list = mouse_interfaces;
+ mouse_simple_driver.endpointsRequested = ENDPOINTS;
+ mouse_simple_driver.requestedEndpoints = mouse_endpoint_requests;
+}
+#else /* defined(OTG_C99) */
+/*! struct usbd_function_operations mouse_function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations mouse_function_ops = {
+ .event_handler = mouse_event_handler, /*!< called for each USB Device Event */
+ .device_request = mouse_device_request, /*!< called for each received device request */
+ .function_enable = mouse_function_enable, /*!< called to enable the function driver */
+ .function_disable = mouse_function_disable, /*!< called to disable the function driver */
+ .endpoint_cleared = mouse_endpoint_cleared,
+};
+
+/*! struct usbd_simple_driver mouse_simple_driver - USB Device Core function driver definition
+ */
+struct usbd_simple_driver mouse_simple_driver = {
+ .driver = {
+ .name = "mouse-random",
+ .fops = &mouse_function_ops, },
+ .device_description = &mouse_fd_device_description, /*!< mouse device description */
+ .bNumConfigurations = sizeof (mouse_fd_configuration_description) / sizeof (struct usbd_configuration_description),
+ .configuration_description = mouse_fd_configuration_description, /*!< mouse configuration description */
+ .interfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = mouse_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = mouse_endpoint_requests,
+};
+#endif /* defined(OTG_C99) */
+
+
+
+/* Module Parameters ******************************************************** */
+/* !
+ * @name XXXXX MODULE Parameters
+ */
+/* ! @{ */
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
+
+static u32 vendor_id; /*!< override built-in vendor ID */
+static u32 product_id; /*!< override built-in product ID */
+
+MOD_PARM (vendor_id, "i");
+MOD_PARM (product_id, "i");
+
+MOD_PARM_DESC (vendor_id, "Device Vendor ID");
+MOD_PARM_DESC (product_id, "Device Product ID");
+
+otg_tag_t MOUSE;
+/* ! *} */
+
+/* USB Module init/exit ***************************************************** */
+
+/*!
+ * mouse_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the mouse driver with the USB Device software.
+ *
+ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse
+ * bottom half handler.
+ *
+ */
+static int mouse_modinit (void)
+{
+ printk (KERN_INFO "%s: vendor_id: %04x product_id: %04x\n", __FUNCTION__, vendor_id, product_id);
+
+ #if !defined(OTG_C99)
+ mouse_global_init();
+ mouse_ops_init();
+ #endif /* defined(OTG_C99) */
+
+ MOUSE = otg_trace_obtain_tag(NULL, "mouse-fd");
+ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x",vendor_id, product_id);
+
+ mouse_fd_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug.....
+
+ // register as usb function driver
+ THROW_IF (usbd_register_simple_function (&mouse_simple_driver, "mouse", NULL), error);
+ TRACE_MSG0(MOUSE, "ok");
+ #ifdef CONFIG_OTG_MOUSE_BH
+ mouse_private.notification_bh.routine = mouse_fd_hid_bh;
+ mouse_private.notification_bh.data = NULL;
+ #endif
+ CATCH(error) {
+ otg_trace_invalidate_tag(MOUSE);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+module_init (mouse_modinit);
+
+#if OTG_EPILOGUE
+/*!
+ * mouse_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ *
+ * @param void
+ * @return void
+ */
+static void mouse_modexit (void)
+{
+ #ifdef CONFIG_OTG_MOUSE_BH
+ while (PENDING_WORK_ITEM(mouse_private.notification_bh)) {
+ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__);
+ schedule_timeout(10 * HZ);
+ }
+ #endif
+ usbd_deregister_simple_function (&mouse_simple_driver);
+
+ otg_trace_invalidate_tag(MOUSE);
+}
+
+module_exit (mouse_modexit);
+#endif
+
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/mouse/mouse-if.c b/drivers/otg/functions/mouse/mouse-if.c
new file mode 100644
index 000000000000..52b7a4684095
--- /dev/null
+++ b/drivers/otg/functions/mouse/mouse-if.c
@@ -0,0 +1,1032 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/mouse/mouse-if.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/mouse/mouse-if.c|20070810225217|38889
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup MouseInterface Mouse Interface Function
+ * @ingroup InterfaceFunctions
+ */
+/*!
+ * @file otg/functions/mouse/mouse-if.c
+ * @brief Mouse Interface Function Driver protocol implementation.
+ *
+ * This file implements the Mouse HID protocols and will generate
+ * random mouse data on the INTERRUPT endpoint.
+ *
+ * The primary purpose of this driver is to demonstrate how to
+ * implement a simple function driver and to provide a simple
+ * uni-directional driver for testing USB Device peripheral drivers.
+ *
+ * The mouse driver has several other characteristics to allow testing of
+ * other features of USB Device peripheral drivers:
+ *
+ * - ep0 delayed CONTROL READ
+ *
+ * The delayed CONTROL READ test verifies that that USB Device peripheral
+ * drivers will correctly NAK until data is provide for device requests.
+ * This is done by (optionally) delaying the HID report via a bottom half
+ * handler. The device request returns normally and the peripheral driver
+ * must properly recognize that while the device request had a non-zero
+ * wLength field, there is currently no queued urb.
+ *
+ * When the bottom half handler is scheduled the HID report urb will be
+ * queued on endpoint zero and then returned to the host.
+ *
+ * The following configuration options are available:
+ *
+ * - CONFIG_OTG_MOUSE_PACKETS is used to set the number of mouse
+ * data reports to send. Set to zero for a continous stream of data.
+ *
+ * - CONFIG_OTG_MOUSE_INTERVAL sets the polling interval for the
+ * interrupt endpoint
+ *
+ * - CONFIG_OTG_BH configures a separate bottom half handler to
+ * respond to HID device requests
+ *
+ *
+ * @ingroup MouseInterface
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-hid.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+
+#define MOUSE mouse_if_trace_tag
+extern otg_tag_t MOUSE;
+
+#ifdef CONFIG_OTG_MOUSE_BH
+atomic_t mouse_if_bh_active;
+#endif /* CONFIG_OTG_MOUSE_BH */
+
+static int Mouse_Interfaces_Active = 0;
+
+/*!@struct mouse_if_private mouse-if.c otg/functions/mouse/mouse-if.c
+ * @brief The mouse_if_private structure is used to collect all of the mouse driver
+ * global variables into one place.
+ */
+struct mouse_if_private {
+
+#ifdef CONFIG_OTG_MOUSE_BH
+ struct WORK_STRUCT notification_bh;
+#endif /* CONFIG_OTG_MOUSE_BH */
+
+ u8 interface_index;
+ u8 bEndpointAddress;
+
+ unsigned int writesize; /*!< packetsize * 4 */
+ int x;
+ int y;
+ int last_x;
+ int last_y;
+ int n;
+ int mouse_count;
+
+ int wLength;
+ u16 pending_report;
+ u8 idle;
+ u8 duration;
+ u16 protocol;
+
+ int mouse_interface_number;
+};
+
+/*!
+ * @name Mouse Descriptors
+ *
+ * MouseHIDReport
+ * MOUSE Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ *
+ * @{
+ */
+
+/*!
+ * MouseHIDReport
+ * This is the HID report returned for the HID Device Request.
+ * There is no pre-defined descriptor type for this.
+ */
+static char MouseHIDReport[52] = {
+ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x02, /*!< USAGE (Mouse) */
+ 0xa1, 0x01, /*!< COLLECTION (Application) */
+ 0x09, 0x01, /*!< USAGE (Pointer) */
+ 0xa1, 0x00, /*!< COLLECTION (Physical) */
+ 0x05, 0x09, /*!< USAGE_PAGE (Button) */
+ 0x19, 0x01, /*!< USAGE_MINIMUM (Button 1) */
+ 0x29, 0x03, /*!< USAGE_MAXIMUM (Button 3) */
+ 0x15, 0x00, /*!< LOGICAL_MINIMUM (0) */
+ 0x25, 0x01, /*!< LOGICAL_MAXIMUM (1) */
+ 0x95, 0x03, /*!< REPORT_COUNT (3) */
+ 0x75, 0x01, /*!< REPORT_SIZE (1) */
+ 0x81, 0x02, /*!< INPUT (Data,Var,Abs) */
+ 0x95, 0x01, /*!< REPORT_COUNT (1) */
+ 0x75, 0x05, /*!< REPORT_SIZE (5) */
+ 0x81, 0x03, /*!< INPUT (Cnst,Var,Abs) */
+ 0x05, 0x01, /*!< USAGE_PAGE (Generic Desktop) */
+ 0x09, 0x30, /*!< USAGE (X) */
+ 0x09, 0x31, /*!< USAGE (Y) */
+ 0x09, 0x38, /*!< USAGE (WHEEL) */
+ 0x15, 0x81, /*!< LOGICAL_MINIMUM (-127) */
+ 0x25, 0x7f, /*!< LOGICAL_MAXIMUM (127) */
+ 0x75, 0x08, /*!< REPORT_SIZE (8) */
+ 0x95, 0x03, /*!< REPORT_COUNT (3) */
+ 0x81, 0x06, /*!< INPUT (Data,Var,Rel) */
+ 0xc0, /*!< END_COLLECTION */
+ 0xc0 /*!< END_COLLECTION */
+};
+
+
+#define BULK_INT 0x00 /*!< Interrupt endpoint number */
+#define ENDPOINTS 0x01 /*!< Number of endpoints required */
+
+/*! List of required endpoint numbers
+ */
+static u8 mouse_if_index[] = { BULK_INT, };
+
+/*! List of requested endpoints
+ */
+#ifndef CONFIG_OTG_MOUSE_INTERVAL
+#define CONFIG_OTG_MOUSE_INTERVAL 1
+#endif /*CONFIG_OTG_MOUSE_INTERVAL*/
+static struct usbd_endpoint_request mouse_if_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_INT, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT, 16, 64, CONFIG_OTG_MOUSE_INTERVAL, },
+ { 0, },
+};
+
+
+#if defined(CONFIG_OTG_NOC99)
+
+/*! This is the HID desccriptor.
+ */
+static struct hid_descriptor mouse_if_hid;
+
+/*! List of class descriptors
+ */
+static struct usbd_generic_class_descriptor *mouse_if_hid_descriptors[] = {
+ (struct usbd_generic_class_descriptor *)&mouse_if_hid, };
+
+
+/*! Interface Descriptions
+ */
+static struct usbd_alternate_description mouse_if_data_alternate_descriptions[1];
+
+/*! List of Interface description(s)
+ */
+static struct usbd_interface_description mouse_interfaces[1];
+
+
+
+/*! mouse_if_global_init -
+ */
+void mouse_if_global_init(void)
+{
+ /*! This is the HID desccriptor.
+ */
+ ZERO(mouse_if_hid);
+ mouse_if_hid.bLength = 0x09;
+ mouse_if_hid.bDescriptorType = HID_DT_HID;
+ mouse_if_hid.bcdHID = __constant_cpu_to_le16(0x101);
+ mouse_if_hid.bCountryCode = 0x00;
+ mouse_if_hid.bNumDescriptors = 0x01;
+ mouse_if_hid.bReportType = HID_DT_REPORT;
+ mouse_if_hid.wItemLength = __constant_cpu_to_le16(sizeof(MouseHIDReport));
+
+
+ /*! Interface Descriptions
+ */
+ ZERO(mouse_if_data_alternate_descriptions);
+
+ mouse_if_data_alternate_descriptions[0].iInterface = "Random Mouse Interface - Interrupt";
+ //mouse_if_data_alternate_descriptions[0].interface_descriptor = &mouse_if_data_alternate_descriptor;
+ mouse_if_data_alternate_descriptions[0].classes =
+ sizeof (mouse_if_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *);
+ mouse_if_data_alternate_descriptions[0].class_list = mouse_if_hid_descriptors;
+ mouse_if_data_alternate_descriptions[0].endpoints = 0x1;
+ mouse_if_data_alternate_descriptions[0].endpoint_index = mouse_if_index;
+
+ mouse_if_data_alternate_descriptions[0].bInterfaceClass = 0x03;
+ mouse_if_data_alternate_descriptions[0].bInterfaceSubClass = 0x01;
+ mouse_if_data_alternate_descriptions[0].bInterfaceProtocol = 0x02;
+
+ /*! List of Interface description(s)
+ */
+ ZERO(mouse_interfaces);
+ mouse_interfaces[0].alternates = sizeof (mouse_if_data_alternate_descriptions) / sizeof (struct usbd_alternate_description);
+ mouse_interfaces[0].alternate_list = mouse_if_data_alternate_descriptions;
+}
+#else /* defined(CONFIG_OTG_NOC99) */
+/*!
+ * mouse_if_hid
+ * This is the HID desccriptor.
+ */
+struct hid_descriptor mouse_if_hid = {
+ .bLength = 0x09,
+ .bDescriptorType = HID_DT_HID,
+ .bcdHID = __constant_cpu_to_le16(0x101),
+
+ .bCountryCode = 0x00,
+ .bNumDescriptors = 0x01,
+ .bReportType = HID_DT_REPORT,
+
+ .wItemLength = __constant_cpu_to_le16(sizeof(MouseHIDReport)),
+};
+
+/*!
+ * mouse_if_hid_descriptors
+ * List of class descriptors
+ */
+static struct usbd_generic_class_descriptor *mouse_if_hid_descriptors[] = {
+ (struct usbd_generic_class_descriptor *)&mouse_if_hid, };
+
+/*!
+ * mouse_if_data_alternate_descriptions
+ * Interface Descriptions
+ */
+static struct usbd_alternate_description mouse_if_data_alternate_descriptions[] = {
+ {
+ /* This string is 31 bytes long which will result in 64 bytes string
+ * useful for testing zlp device requests
+ *
+ * 0123456789abcdef0123456789abcdef
+ * | |
+ */
+ .iInterface = "Belcarra Random Mouse Interface",
+ .bInterfaceClass = 0x03,
+ .bInterfaceSubClass = 0x01,
+ .bInterfaceProtocol = 0x02,
+ .classes = sizeof (mouse_if_hid_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = mouse_if_hid_descriptors,
+ .endpoints = sizeof (mouse_if_index) / sizeof(u8),
+ .endpoint_index = mouse_if_index,
+ },
+};
+
+/*!
+ * mouse_interfaces
+ * List of Interface description(s)
+ */
+static struct usbd_interface_description mouse_interfaces[] = {
+ { .alternates = sizeof (mouse_if_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = mouse_if_data_alternate_descriptions,},
+};
+
+#endif /* defined(CONFIG_OTG_NOC99) */
+
+/*! @} */
+
+/* MOUSE ***************************************************************************************** */
+/*!
+ * @name Transmit Function
+ *
+ * @{
+ */
+
+/*!
+ * get_xy
+ * Random walk array
+ */
+static int get_xy[8] = {
+ 0, 0, 1, 1,
+ 0, 0, -1, -1,
+};
+
+
+
+extern void mouse_if_send(struct usbd_function_instance *function);
+
+/*!
+ * @brief mouse_urb_sent() - called to indicate URB transmit finished
+ * This function is the callback function for sent urbs.
+ *
+ * It will continue to queue new urbs until either there is an error or the
+ * maximum count is exceeded.
+ *
+ * @param tx_urb The urb to be sent.
+ * @param rc result
+ * @return int Return non-zero for failure.
+ */
+static int mouse_urb_sent (struct usbd_urb *tx_urb, int rc)
+{
+ struct usbd_function_instance *function = tx_urb->function_instance;
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+
+ TRACE_MSG4(MOUSE, "mouse_count[%d]: count: %d status: %d state: %d",
+ interface_instance->wIndex, mouse->mouse_count,
+ usbd_get_device_status(function), usbd_get_device_state(function));
+ // return non-zero to get tx_urb deallocated
+ RETURN_EINVAL_IF(usbd_get_device_status(function) == USBD_CLOSING);
+ RETURN_EINVAL_IF(usbd_get_device_status(function) != USBD_OK);
+ RETURN_EINVAL_IF(usbd_get_device_state(function) != STATE_CONFIGURED);
+
+ //TRACE_MSG2(MOUSE, "mouse_count[%d]: %d", interface_instance->wIndex, mouse->mouse_count);
+ usbd_free_urb(tx_urb);
+ #ifdef CONFIG_OTG_MOUSE_PACKETS
+ RETURN_ZERO_IF (CONFIG_OTG_MOUSE_PACKETS && (mouse->mouse_count++ > CONFIG_OTG_MOUSE_PACKETS));
+ #endif /* CONFIG_OTG_MOUSE_PACKETS */
+ mouse_if_send(function); // re-start
+ return 0;
+}
+/*!
+ * mouse_if_send() - send a mouse data urb with random data
+ *
+ * Queue a new data urb to send small mouse movement to host.
+ *
+ * @param function
+ * @return void
+ */
+void mouse_if_send(struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ int new_x = 0;
+ int new_y = 0;
+ u8 random;
+
+ struct usbd_urb *tx_urb;
+
+ tx_urb = usbd_alloc_urb (function, BULK_INT, 4, mouse_urb_sent);
+ RETURN_UNLESS(tx_urb);
+
+ memset(tx_urb->buffer, 0, 4);
+
+ /* when n is zero, generate new directional movement, and new n, from random data */
+ UNLESS (mouse->n) {
+ otg_get_random_bytes(&random, 1);
+
+ mouse->last_x = MAX(-4, MIN(4, mouse->last_x + get_xy[random & 0x7]));
+ mouse->last_y = MAX(-4, MIN(4, mouse->last_y + get_xy[(random >> 3) & 0x7]));
+ mouse->n = (random>>6) & 0x3;
+
+ new_x = mouse->x + mouse->last_x;
+ new_y = mouse->y + mouse->last_y;
+
+ if (Mouse_Interfaces_Active > 1) {
+ if (mouse->mouse_interface_number & 0x1)
+ tx_urb->buffer[1] = mouse->last_x;
+ else
+ tx_urb->buffer[2] = mouse->last_y;
+ }
+ else {
+ tx_urb->buffer[1] = mouse->last_x;
+ tx_urb->buffer[2] = mouse->last_y;
+
+ }
+ mouse->x = new_x;
+ mouse->y = new_y;
+ }
+ /* when n is odd, simply send zero's */
+ else if (mouse->n & 0x1) {
+ mouse->n--;
+ }
+ /* when n is non-zero and not odd, send directional movement */
+ else {
+ mouse->n--;
+ tx_urb->buffer[1] = mouse->last_x;
+ tx_urb->buffer[2] = mouse->last_y;
+ mouse->x = new_x;
+ mouse->y = new_y;
+ }
+ tx_urb->actual_length = 4;
+ TRACE_MSG0(MOUSE, "STARTING");
+ RETURN_UNLESS(usbd_start_in_urb(tx_urb));
+ TRACE_MSG0(MOUSE, "FAILED");
+ usbd_free_urb(tx_urb);
+}
+
+/*! @} */
+
+/* USB Device Functions ************************************************************************ */
+
+
+/*! copy_report()
+ * This function copies the Mouse HID report into the provided URB.
+ * @param urb Destination
+ * @param data Source
+ * @param size Amount of data to copy.
+ * @param max_buf Size of buffer
+ * @return Non-zero if error.
+ *
+ */
+static int copy_report (struct usbd_urb *urb, void *data, int size, int max_buf)
+{
+ int available;
+ int length;
+
+ RETURN_EINVAL_IF (!urb);
+ RETURN_EINVAL_IF (!data);
+ RETURN_EINVAL_IF (!(length = size));
+ RETURN_EINVAL_IF ((available = max_buf - urb->actual_length) <= 0);
+
+ TRACE_MSG3(MOUSE, "max_buf: %d actual: %d available: %d", max_buf, urb->actual_length, available);
+ length = (length < available) ? length : available;
+ memcpy (urb->buffer + urb->actual_length, data, length);
+ urb->actual_length += length;
+ return 0;
+}
+
+/*!
+ * mouse_if_send_hid_descriptor() - send an EP0 urb containing HID descriptor
+ * This is called to send the Mouse HID report.
+ *
+ * This will satisfy a device request from the host.
+ *
+ * @param function - pointer to function instance
+ * @return int
+ */
+static int mouse_if_send_hid_descriptor (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ struct usbd_urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL);
+ int hs = usbd_high_speed(function);
+ int wMaxPacketSize = usbd_endpoint_zero_wMaxPacketSize(function, hs);
+
+ TRACE_MSG2(MOUSE, "Send Hid wLength: %d wMaxPacketSize: %d", mouse->wLength, wMaxPacketSize);
+ TRACE_MSG0(MOUSE, "AAAA");
+ RETURN_EINVAL_IF (copy_report(urb, (u8 *)&mouse_if_hid, sizeof(mouse_if_hid), mouse->wLength));
+ TRACE_MSG1(MOUSE, "BBBB actual: %d", urb->actual_length);
+ RETURN_EINVAL_UNLESS (wMaxPacketSize);
+ TRACE_MSG0(MOUSE, "BBBB");
+
+ if (!(urb->actual_length % wMaxPacketSize) && (urb->actual_length < mouse->wLength))
+ urb->flags |= USBD_URB_SENDZLP;
+
+ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
+ TRACE_MSG0(MOUSE, "CCCC");
+ usbd_free_urb(urb);
+ return -EINVAL;
+}
+
+/*!
+ * mouse_if_send_hid_report() - send an EP0 urb containing HID report
+ * This is called to send the Mouse HID report.
+ *
+ * This will satisfy a device request from the host.
+ *
+ * @param function - pointer to function instance
+ * @return int
+ */
+static int mouse_if_send_hid_report (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ struct usbd_urb *urb = usbd_alloc_urb_ep0(function, mouse->wLength, NULL);
+ int hs = usbd_high_speed(function);
+ int wMaxPacketSize = usbd_endpoint_zero_wMaxPacketSize(function, hs);
+
+ TRACE_MSG1(MOUSE, "Send Hid wLength: %d", mouse->wLength);
+ TRACE_MSG0(MOUSE, "AAAA");
+ RETURN_EINVAL_IF (copy_report(urb, MouseHIDReport, sizeof(MouseHIDReport), mouse->wLength));
+ TRACE_MSG0(MOUSE, "BBBB");
+ RETURN_EINVAL_UNLESS (wMaxPacketSize);
+ TRACE_MSG0(MOUSE, "BBBB");
+
+ if (!(urb->actual_length % wMaxPacketSize) && (urb->actual_length < mouse->wLength))
+ urb->flags |= USBD_URB_SENDZLP;
+
+ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
+ TRACE_MSG0(MOUSE, "CCCC");
+ usbd_free_urb(urb);
+ return -EINVAL;
+}
+
+#ifdef CONFIG_OTG_MOUSE_BH
+/*!
+ * @brief mouse_if_hid_bh() - Bottom half handler to send a HID report
+ * Bottom half handler.
+ *
+ * Ssne device request response from bottom half. This tests
+ * delayed device request IN responses.
+ *
+ * @param data
+ * @return none
+ */
+static void mouse_if_hid_bh (void *data)
+{
+ struct usbd_function_instance *function = (struct usbd_function_instance *)data;
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ mouse_if_send_hid_report(function);
+ atomic_inc(&mouse_if_bh_active);
+}
+
+/*! mouse_schedule_bh - schedule a call for mouse_if_hid_bh
+ * This will schedule the mouse bottom half handler.
+ *
+ * @param void
+ * @return 0 on success
+ */
+static int mouse_schedule_bh (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+
+ TRACE_MSG0(MOUSE, "Scheduling");
+ TRACE_MSG0(MOUSE, "AAAA");
+ PREPARE_WORK_ITEM(mouse->notification_bh, mouse_if_hid_bh, (void *)function);
+ atomic_set(&mouse_if_bh_active, 1);
+ SCHEDULE_WORK(mouse->notfication_bh);
+ TRACE_MSG0(MOUSE, "BBBB");
+ return 0;
+}
+#endif /* CONFIG_OTG_MOUSE_BH */
+
+/*!
+ * @brief mouse_report_received() - called to indicate URB transmit finished
+ * This function is the callback function for sent urbs.
+ * It simply queues up another urb until the packets to be sent
+ * configuration parameter is reached (or forever if zero.)
+ * @param rx_urb The urb to be sent.
+ * @param rc result
+ * @return int Return non-zero for failure.
+ */
+int mouse_report_received (struct usbd_urb *rx_urb, int rc)
+{
+ struct usbd_function_instance *function;
+ struct usbd_interface_instance *interface_instance;
+ struct mouse_if_private *mouse;
+ function = rx_urb->function_instance;
+ interface_instance = (struct usbd_interface_instance *)function;
+ mouse = interface_instance->function.privdata;
+ TRACE_MSG3(MOUSE, "REPORT[%02x]: len: %d buf[0]: %x",
+ mouse->pending_report, rx_urb->actual_length, rx_urb->buffer[0]);
+ usbd_free_urb(rx_urb);
+ mouse->pending_report = 0;
+ return 0;
+}
+
+
+/*!
+ * mouse_if_device_request - called to process a request to endpoint or interface
+ * Process a device request.
+ *
+ * The HID host sends various device requests. We only support a limited
+ * set.
+ *
+ * @param function
+ * @param request
+ * @return non-zero for failure, will cause endpoint zero stall
+ */
+static int mouse_if_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ struct usbd_urb *urb = NULL;
+
+ TRACE_MSG6(MOUSE, "Interface[%02d] bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x wLength: %04x",
+ interface_instance->wIndex, request->bmRequestType, request->bRequest, request->wValue,
+ request->wIndex, request->wLength);
+
+ /* HID only sends requests to interface.
+ */
+ TRACE_MSG0(MOUSE, "CHECK RECIP");
+ RETURN_EINVAL_UNLESS(USB_REQ_RECIPIENT_INTERFACE == (request->bmRequestType & USB_REQ_RECIPIENT_MASK));
+
+
+ TRACE_MSG0(MOUSE, "RECIP OK");
+
+ switch (request->bmRequestType & USB_REQ_DIRECTION_MASK) {
+ case USB_REQ_DEVICE2HOST:
+ TRACE_MSG0(MOUSE, "DEVICE2HOST");
+ switch (request->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ TRACE_MSG0(MOUSE, "GET DESCRIPTOR");
+ switch (le16_to_cpu(request->wValue)>>8) {
+ case HID_DT_HID:
+ mouse->wLength = request->wLength;
+ return mouse_if_send_hid_descriptor(function);
+
+ case HID_DT_REPORT:
+ TRACE_MSG0(MOUSE, "GET DESCRIPTOR HID REPORT");
+ mouse->wLength = le16_to_cpu(request->wLength);
+ #ifdef CONFIG_OTG_MOUSE_BH
+ return mouse_schedule_bh(function);
+ #else
+ return mouse_if_send_hid_report(function);
+ #endif
+ default:
+ TRACE_MSG0(MOUSE, "GET DESCRIPTOR HID UNKNOWN");
+ break;
+ }
+ return -EINVAL;
+ case USB_REQ_GET_REPORT:
+ TRACE_MSG0(MOUSE, "GET REPORT"); break;
+ case USB_REQ_GET_IDLE:
+ TRACE_MSG0(MOUSE, "GET IDLE"); break;
+ case USB_REQ_GET_PROTOCOL:
+ TRACE_MSG0(MOUSE, "GET PROTOCOL"); break;
+ break;
+ default:
+ TRACE_MSG0(MOUSE, "GET DEFAULT"); break;
+ return -EINVAL;
+ }
+
+ RETURN_EINVAL_UNLESS(request->wLength &&
+ ((urb = usbd_alloc_urb_ep0 (function, request->wLength, NULL))));
+
+ switch (request->bRequest) {
+
+ case USB_REQ_GET_REPORT:
+
+ switch (le16_to_cpu(request->wValue) >> 8) {
+ case HID_INPUT:
+ TRACE_MSG0(MOUSE, "GET REPORT HID_INPUT"); break;
+ case HID_OUTPUT:
+ TRACE_MSG0(MOUSE, "GET REPORT HID_OUTPUT"); break;
+ case HID_FEATURE:
+ TRACE_MSG0(MOUSE, "GET REPORT HID_FEATURE"); break;
+ break;
+ default:
+ TRACE_MSG0(MOUSE, "GET REPORT default"); break;
+ break;
+ }
+ // XXX create urb and send?
+ urb->actual_length = 1;
+ break;
+
+ case USB_REQ_GET_IDLE:
+ case USB_REQ_GET_PROTOCOL:
+ urb->actual_length = 1;
+ switch (request->bRequest) {
+ case USB_REQ_GET_IDLE:
+ urb->buffer[0] = cpu_to_le16(mouse->idle);
+ TRACE_MSG1(MOUSE, "GET IDLE: %x", urb->buffer[0]);
+ break;
+ case USB_REQ_GET_PROTOCOL:
+ urb->buffer[0] = cpu_to_le16(mouse->protocol);
+ TRACE_MSG1(MOUSE, "GET PROTOCOL: %x", urb->buffer[0]);
+ break;
+ }
+ }
+
+ RETURN_ZERO_UNLESS(usbd_start_in_urb(urb));
+
+ /* only get here if error */
+ usbd_free_urb(urb);
+ TRACE_MSG0(MOUSE, "get failed");
+ break;
+
+ case USB_REQ_HOST2DEVICE:
+ TRACE_MSG0(MOUSE, "HOST2DEVICE");
+ switch (request->bRequest) {
+ case USB_REQ_SET_REPORT:
+ TRACE_MSG0(MOUSE, "SET REPORT");
+ // Sample: 21 09 00 02 00 00 01 00
+ mouse->pending_report = le16_to_cpu(request->wValue);
+ RETURN_EINVAL_UNLESS(request->wLength &&
+ (urb = usbd_alloc_urb_ep0(function, request->wLength, mouse_report_received)));
+
+ RETURN_ZERO_UNLESS (usbd_start_out_urb (urb));
+ /* failure */
+ usbd_free_urb (urb);
+ mouse->pending_report = 0;
+ return -EINVAL;
+
+ case USB_REQ_SET_IDLE:
+ TRACE_MSG0(MOUSE, "SET IDLE");
+ mouse->idle = le16_to_cpu(request->wValue) >> 8;
+ mouse->duration = le16_to_cpu(request->wValue) & 0xff;
+ TRACE_MSG2(MOUSE, "SET IDLE: idle: %02x duration: %02x", mouse->idle, mouse->duration);
+ return 0;
+
+ case USB_REQ_SET_PROTOCOL:
+ TRACE_MSG0(MOUSE, "SET PROTOCOL");
+ mouse->protocol = le16_to_cpu(request->wValue);
+ TRACE_MSG1(MOUSE, "SET PROTOCOL: %x", request->wValue);
+ return 0;
+ default:
+ TRACE_MSG0(MOUSE, "SET UNKNOWN");
+ break;
+ }
+ break;
+ }
+ return -EINVAL;
+}
+
+/*!
+ * @brief mouse_if_set_configuration - device configured
+ * Called when the host performs a set configuration.
+ *
+ * This will start sending data.
+ *
+ * @param function
+ * @param configuration
+ * @return int
+ */
+static int mouse_if_set_configuration (struct usbd_function_instance *function, int configuration)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ int hs = usbd_high_speed(function);
+
+
+ TRACE_MSG3(MOUSE, "SET_CONFIGURATION MOUSE_IF[%d] %x cfg: %d ", interface_instance->wIndex, function, configuration);
+
+ // XXX Need to differentiate between non-zero, zero and non-zero done twice
+
+ function->privdata = mouse;;
+
+ mouse->n = mouse->x = mouse->y = mouse->last_x = mouse->last_y = 0;
+
+ mouse->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, BULK_INT, hs);
+ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, hs);
+
+ mouse->mouse_count = 0;
+
+ TRACE_MSG4(MOUSE, "MOUSE_IF[%2d] Configured: %d bEndpointAddress: %02x size: %02x",
+ interface_instance->wIndex, configuration, mouse->bEndpointAddress, mouse->writesize);
+
+ mouse_if_send(function); // start sending
+
+ return 0;
+}
+
+/*!
+ * @brief mouse_if_reset - called to indicate bus has been reset
+ * @param function_instance
+ * @return int
+ */
+int mouse_if_reset (struct usbd_function_instance *function_instance)
+{
+ TRACE_MSG0(MOUSE,"RESET");
+ return 0;
+}
+
+
+/*!
+ * @brief mouse_if_suspended - called to indicate bus has been suspended
+ * @param function_instance
+ * @return int
+ */
+int mouse_if_suspended (struct usbd_function_instance *function_instance)
+{
+ TRACE_MSG0(MOUSE,"SUSPENDED");
+ return 0;
+}
+
+
+
+/*!
+ * @brief mouse_if_endpoint_cleared - called by the USB Device Core when endpoint cleared
+ * Clear an endpoint.
+ *
+ * @param function The function instance for this driver
+ * @param bEndpointAddress
+ * @return none
+ */
+static void mouse_if_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress)
+{
+ //struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ //struct mouse_if_private *mouse = interface_instance->function.privdata;
+ TRACE_MSG1(MOUSE,"CLEARED bEndpointAddress: %02x", bEndpointAddress);
+ mouse_if_send(function); // re-start sending
+}
+
+
+
+/* ********************************************************************************************* */
+/*! mouse_if_function_enable - called by USB Device Core to enable the driver
+ * Called by the USBD core layer to enable this driver.
+ *
+ * @param function The function instance for this driver to use.
+ * @return non-zero if error.
+ */
+static int mouse_if_function_enable (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = NULL;
+ int hs = usbd_high_speed(function);
+
+ RETURN_EINVAL_UNLESS((mouse = CKMALLOC(sizeof(struct mouse_if_private))));
+ // XXX MODULE LOCK HERE
+ interface_instance->function.privdata = (void *)mouse;
+ mouse->writesize = usbd_endpoint_wMaxPacketSize(function, BULK_INT, hs);
+ mouse->mouse_interface_number = Mouse_Interfaces_Active++;
+ return 0;
+}
+
+/*! mouse_if_function_disable - called by the USB Device Core to disable the driver
+ * Called by the USBD core layer to disable this driver.
+ *
+ * @param function The function instance for this driver
+ */
+static void mouse_if_function_disable (struct usbd_function_instance *function)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function;
+ struct mouse_if_private *mouse = interface_instance->function.privdata;
+ interface_instance->function.privdata = NULL;
+ Mouse_Interfaces_Active--;
+ LKFREE(mouse);
+ // XXX MODULE UNLOCK HERE
+}
+
+/* ********************************************************************************************* */
+const char *mouse_usb_strings[4] = {
+ "Mouse String Test 2",
+ "Mouse String Test 4",
+ "Mouse String Test 6",
+ NULL,
+};
+u8 mouse_usb_string_indexes[4] = {
+ 2, 4, 6, 0,
+};
+
+#if defined(CONFIG_OTG_NOC99)
+/*! function_ops - operations table for the USB Device Core
+ */
+static struct usbd_function_operations mouse_function_ops;
+
+/*! mouse_interface_driver - USB Device Core function driver definition
+ */
+struct usbd_interface_driver mouse_interface_driver;
+
+/*! mouse_if_ops_init - initialize mouse interface operations
+ * @param void
+ * @return none
+ */
+void mouse_if_ops_init(void)
+{
+ /*! function_ops - operations table for the USB Device Core
+ */
+
+ ZERO(mouse_function_ops);
+ mouse_function_ops.set_configuration = mouse_if_set_configuration;
+ mouse_function_ops.device_request = mouse_if_device_request; /*! called for each received device request */
+ mouse_function_ops.endpoint_cleared = mouse_if_endpoint_cleared;
+
+ mouse_function_ops.function_enable = mouse_if_function_enable;
+ mouse_function_ops.function_disable = mouse_if_function_disable;
+
+ /*! interface_driver - USB Device Core function driver definition
+ */
+ ZERO(mouse_interface_driver);
+ mouse_interface_driver.driver.name = "mouse-random-if"; /*! driver name */
+ mouse_interface_driver.driver.fops = &mouse_function_ops; /*! operations table */
+ mouse_interface_driver.driver.usb_strings = mouse_usb_strings; /*! operations table */
+ mouse_interface_driver.driver.usb_string_indexes = mouse_usb_string_indexes; /*! operations table */
+
+ mouse_interface_driver.interfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description);
+ mouse_interface_driver.interface_list = mouse_interfaces;
+ mouse_interface_driver.endpointsRequested = ENDPOINTS;
+ mouse_interface_driver.requestedEndpoints = mouse_if_endpoint_requests;
+ mouse_interface_driver.iFunction = "Belcarra Random Mouse - 1.0 HID";
+
+}
+
+#else /* defined(CONFIG_OTG_NOC99) */
+/*! function_ops - operations table for the USB Device Core
+ * The set of interface function operations supported by this driver.
+ */
+static struct usbd_function_operations mouse_function_ops = {
+ .set_configuration = mouse_if_set_configuration,
+ .device_request = mouse_if_device_request, /*!< called for each received device request */
+ .endpoint_cleared = mouse_if_endpoint_cleared,
+ .function_enable = mouse_if_function_enable,
+ .function_disable = mouse_if_function_disable,
+};
+
+/*! mouse_interface_driver - USB Device Core function driver definition
+ * The interface function configuration for this driver.
+ */
+struct usbd_interface_driver mouse_interface_driver = {
+ .driver = {
+ .name = "mouse-random-if",
+ .fops = &mouse_function_ops,
+ .usb_strings = mouse_usb_strings,
+ .usb_string_indexes = mouse_usb_string_indexes,
+ },
+ .interfaces = sizeof (mouse_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = mouse_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = mouse_if_endpoint_requests,
+
+ .bFunctionClass = 0x03,
+ .bFunctionSubClass = 0x01,
+ .bFunctionProtocol = 0x02,
+ .iFunction = "Belcarra Random Mouse - 1.0 HID",
+};
+#endif /* defined(CONFIG_OTG_NOC99) */
+
+
+/*! Module Parameters ******************************************************** */
+
+/*! @name XXXXX MODULE Parameters
+ */
+/*@{*/
+
+MOD_AUTHOR ("sl@belcarra.com");
+EMBED_LICENSE();
+MOD_DESCRIPTION ("Belcarra Random Walk MOUSE Function");
+
+MOD_PARM_INT (vendor_id, "Device Vendor ID", 0);
+MOD_PARM_INT (product_id, "Device Product ID", 0);
+
+otg_tag_t MOUSE;
+
+/*! @} */
+
+/*! @name USB Module init/exit ***************************************************** */
+
+ /*! @{
+ */
+
+/*!
+ * mouse_if_modinit() - module init
+ *
+ * This is called by the Linux kernel; either when the module is loaded
+ * if compiled as a module, or during the system intialization if the
+ * driver is linked into the kernel.
+ *
+ * This function will parse module parameters if required and then register
+ * the mouse driver with the USB Device software.
+ *
+ * If the CONFIG_OTG_MOUSE_BH option is enabled it will also setup the mouse
+ * bottom half handler.
+ */
+int mouse_if_modinit (void)
+{
+ #if defined(CONFIG_OTG_NOC99)
+ mouse_if_global_init();
+ mouse_if_ops_init();
+ #endif /* defined(CONFIG_OTG_NOC99) */
+
+ MOUSE = otg_trace_obtain_tag(NULL, "mouse-if");
+ TRACE_MSG2(MOUSE, "vendor_id: %04x product_id: %04x", MODPARM(vendor_id), MODPARM(product_id));
+
+ mouse_if_hid.wItemLength = cpu_to_le16(0x34); // XXX mips compiler bug.....
+
+ // register as usb function driver
+ TRACE_MSG0(MOUSE, "REGISTER INTERFACE");
+ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.driver.name, mouse_interface_driver.endpointsRequested);
+
+ THROW_IF (usbd_register_interface_function ( &mouse_interface_driver, "mouse-if", NULL), error);
+
+ TRACE_MSG2(MOUSE, "%s %d", mouse_interface_driver.driver.name, mouse_interface_driver.endpointsRequested);
+
+ TRACE_MSG0(MOUSE, "REGISTER FINISHED");
+ #ifdef CONFIG_OTG_MOUSE_BH
+ mouse_if_private.notification_bh.routine = mouse_if_hid_bh;
+ mouse_if_private.notification_bh.data = NULL;
+ #endif
+ CATCH(error) {
+ otg_trace_invalidate_tag(MOUSE);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+module_init (mouse_if_modinit);
+
+/*!
+ * mouse_if_modexit() - module init
+ *
+ * This is called by the Linux kernel; when the module is being unloaded
+ * if compiled as a module. This function is never called if the
+ * driver is linked into the kernel.
+ *
+ * @return void
+ */
+void mouse_if_modexit (void)
+{
+ #ifdef CONFIG_OTG_MOUSE_BH
+ while (atomic_read(&mouse_if_bh_active)) {
+ printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__);
+ schedule_timeout(10 * HZ);
+ }
+ #endif
+ usbd_deregister_interface_function (&mouse_interface_driver);
+ otg_trace_invalidate_tag(MOUSE);
+}
+
+#if OTG_EPILOGUE
+module_exit (mouse_if_modexit);
+#endif
+/*! @} */
diff --git a/drivers/otg/functions/mouse/otg-config.h b/drivers/otg/functions/mouse/otg-config.h
new file mode 100644
index 000000000000..33d24d40d4d3
--- /dev/null
+++ b/drivers/otg/functions/mouse/otg-config.h
@@ -0,0 +1,143 @@
+/*
+ * 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
+ */
+/*
+# @(#) balden@belcarra.com|otg/functions/mouse/otg-config.h|20060419204257|26035
+ *
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ */
+
+/*
+ * tristate " Random Mouse Function"
+ * This implements a simple Mouse driver that sends HID packets with
+ * small random movements. This is a good function for initial testing
+ * of Peripheral Controller Drivers as it provides for a complicated
+ * enumeration but a simple uni-directional (send Interrupt only) data
+ * transport.
+ */
+/* #define CONFIG_OTG_MOUSE */
+
+/*
+ * bool "Build an interface module"
+ * default "y"
+ * This module needs a composite function module, such as
+ * mouse_cf or generic_cf to be activated
+ */
+#define CONFIG_OTG_MOUSE_INTERFACE
+
+/*
+ * bool "Build a simple OTG 2.x style composite function with mouse"
+ * default "n"
+ * This module provides a very simple composite function to drive
+ * the mouse_if module (see above). The generic_cf module provides
+ * a much richer composite function supporting multiple functions.
+ */
+#define CONFIG_OTG_MOUSE_COMPOSITE
+
+/*
+ * bool "Build an OTG 1.x style function module/driver"
+ * depends on OTG && OTG_MOUSE
+ * default "n"
+ * This is the mouse function driver from previous releases. It
+ * does not use the composite/interface mechanism. If this module
+ * is used, neither of the other two mouse modules is required.
+ */
+#define CONFIG_OTG_MOUSE_TRADITIONAL
+
+/*
+ * hex "VendorID (hex value)"
+ * default "0x15ec"
+ * This parameter is not used by the mouse_if module because the
+ * composite layer is responsible for setting this parameter
+ */
+#define CONFIG_OTG_MOUSE_VENDORID 0x15ec
+
+/*
+ * hex "ProductID (hex value)"
+ * default "0xf003"
+ * Parameters such as PRODUCTID, VENDORID are now the responsibility
+ * of the composite driver rather than the interface driver. For this
+ * reason, the module parameters vendor_id, etc are not available in
+ * the interface versions. Therefore, appropriate values should be set
+ * in the kernel configuration. These configuration parameters
+ * are referenced in both mouse_cf and generic_cf.
+ */
+#define CONFIG_OTG_MOUSE_PRODUCTID 0xf003
+
+/*
+ * hex "bcdDevice (binary-coded decimal)"
+ * default "0x0100"
+ */
+#define CONFIG_OTG_MOUSE_BCDDEVICE 0x0100
+
+/*
+ * string "iManufacturer (string)"
+ * default "Belcarra"
+ */
+#define CONFIG_OTG_MOUSE_MANUFACTURER "Belcarra"
+
+/*
+ * string "iProduct (string)"
+ * default "Random Mouse Device"
+ */
+#define CONFIG_OTG_MOUSE_PRODUCT_NAME "Random Mouse Device"
+
+/*
+ * string "MOUSE Bulk Only iInterface (string)"
+ * default "MOUSE Data Intf"
+ */
+#define CONFIG_OTG_MOUSE_COMM_INTF "MOUSE Data Intf"
+
+/*
+ * string "Data Interface iConfiguration (string)"
+ * default "MOUSE Configuration"
+ */
+#define CONFIG_OTG_MOUSE_DESC "MOUSE Configuration"
+
+/*
+ * bool " MOUSE BH Test"
+ * default n
+ * Implement the Mouse send packet in a bottom half handler,
+ * this will delay responses to test the PCD works correctly
+ * when the host polls before a send data urb is queued.
+ */
+#define CONFIG_OTG_MOUSE_BH
+
+/*
+ * int "Number of packets (zero is continous)"
+ * default 10
+ * Number of Mouse packets to send, will run
+ * forever if set to zero.
+ */
+#define CONFIG_OTG_MOUSE_PACKETS 10
+
+/*
+ * bool " MOUSE Stall Test"
+ * default n
+ * Periodically stall the INTERRUPT endpoint.
+ * Used for testing.
+ */
+#define CONFIG_OTG_MOUSE_STALL
+
+/*
+ * int "Number of Stalls"
+ * default 1
+ * Number of Stall tests to perform.
+ */
+#define CONFIG_OTG_MOUSE_STALL_COUNT
+
+/*
+ * int 'Polling Interval '
+ * default 1
+ * Sets interrupt endpoint interval.
+ */
+#define CONFIG_OTG_MOUSE_INTERVAL 1
diff --git a/drivers/otg/functions/msc/Kconfig b/drivers/otg/functions/msc/Kconfig
new file mode 100644
index 000000000000..321d4bc30569
--- /dev/null
+++ b/drivers/otg/functions/msc/Kconfig
@@ -0,0 +1,63 @@
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/functions/msc/Kconfig|20070531215019|51512
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+menu "OTG Mass Storage function"
+ depends on OTG
+
+config OTG_MSC
+ tristate "Mass Storage Function"
+ depends on OTG
+
+config OTG_MSC_PIPES_TEST
+ bool "Pipes Test Only"
+ depends on OTG && OTG_MSC && OTG_INTERNAL_TESTING
+ default n
+ ---help---
+ Setting this allows the pipes to be tested separately.
+
+menu "OTG Mass Storage function options"
+ depends on OTG && OTG_MSC
+
+#config OTG_MSC_VENDORID
+# hex "VendorID (hex value)"
+# depends on OTG && OTG_MSC
+# default "0x15ec"
+
+#config OTG_MSC_PRODUCTID
+# hex "ProductID (hex value)"
+# depends on OTG && OTG_MSC
+# default "0xf006"
+
+#config OTG_MSC_BCDDEVICE
+# hex "bcdDevice (binary-coded decimal)"
+# depends on OTG && OTG_MSC
+# default "0x0100"
+
+
+
+config OTG_MSC_MANUFACTURER
+ string "iManufacturer (string)"
+ depends on OTG && OTG_MSC
+ default "Belcarra"
+
+config OTG_MSC_PRODUCT_NAME
+ string "iProduct (string)"
+ depends on OTG && OTG_MSC
+ default "Mass Storage Class - Bulk Only"
+
+config OTG_MSC_INTF
+ string "MSC Bulk Only iInterface (string)"
+ depends on OTG && OTG_MSC
+ default "MSC BO Data Intf"
+
+#config OTG_MSC_DESC
+# string "Data Interface iConfiguration (string)"
+# depends on OTG && OTG_MSC
+# default "MSC BO Configuration"
+
+config OTG_MSC_REGISTER_TRACE
+ bool "MSC Tracing"
+ depends on OTG && OTG_MSC && OTG_INTERNAL_TESTING
+ default n
+endmenu
+
+endmenu
diff --git a/drivers/otg/functions/msc/Makefile b/drivers/otg/functions/msc/Makefile
new file mode 100644
index 000000000000..f49791849738
--- /dev/null
+++ b/drivers/otg/functions/msc/Makefile
@@ -0,0 +1,17 @@
+# Function driver for a Random Mouse
+# @(#) balden@belcarra.com|otg/functions/msc/Makefile-l26|20060419204258|01484
+#
+# Copyright (c) 2004 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+msc_if-objs := msc-fd.o crc.o msc-linux.o msc-bo.o msc-io-l24.o
+
+
+obj-$(CONFIG_OTG_MSC) += msc_if.o
+
+OTG=$(TOPDIR)/drivers/otg
+ACMD=$(OTG)/functions/msc
+OTGCORE_DIR=$(OTG)/otgcore
+USBDCORE_DIR=$(OTG)/usbdcore
+EXTRA_CFLAGS += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
+EXTRA_CFLAGS_nostdinc += -I$(ACMD) -I$(OTG) -Wno-unused -Wno-format -I$(USBDCORE_DIR) -I$(OTGCORE_DIR)
diff --git a/drivers/otg/functions/msc/crc.c b/drivers/otg/functions/msc/crc.c
new file mode 100644
index 000000000000..47c101b4c6c5
--- /dev/null
+++ b/drivers/otg/functions/msc/crc.c
@@ -0,0 +1,95 @@
+/*
+ * 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
+ */
+/*
+ * otg/msc_fd/crc.c - crc table
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/crc.c|20070425221028|06858
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include <otg/otg-compat.h>
+
+//#include <usbd-chap9.h>
+//#include <usbd-mem.h>
+//#include <usbd.h>
+//#include <usbd-func.h>
+
+#include "crc.h"
+
+unsigned int *msc_crc32_table;
+
+/**
+ * Generate the crc32 table
+ *
+ * return non-zero if malloc fails
+ */
+int make_crc_table(void)
+{
+ unsigned int n;
+ if (msc_crc32_table) return 0;
+ if (!(msc_crc32_table = (unsigned int *)ckmalloc(256*4))) return -EINVAL;
+ for (n = 0; n < 256; n++) {
+ int k;
+ unsigned int c = n;
+ for (k = 0; k < 8; k++) {
+ c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1);
+ }
+ msc_crc32_table[n] = c;
+ }
+ return 0;
+}
+/*! free_crc_table - free ckmalloced resource for crc-table
+ * @return void
+ */
+void free_crc_table(void)
+{
+ if (msc_crc32_table) {
+ lkfree(msc_crc32_table);
+ msc_crc32_table = NULL;
+ }
+}
+
+/*! crc32_compute - compute frame check sequence number
+ * @param src source data
+ * @param len length of source data
+ * @param val FCS value
+ * @return FCS result
+ */
+
+unsigned int crc32_compute(unsigned char *src, int len, unsigned int val)
+{
+ for (; len-- > 0; val = COMPUTE_FCS (val, *src++));
+ return val;
+}
diff --git a/drivers/otg/functions/msc/crc.h b/drivers/otg/functions/msc/crc.h
new file mode 100644
index 000000000000..2c7ed4a57569
--- /dev/null
+++ b/drivers/otg/functions/msc/crc.h
@@ -0,0 +1,40 @@
+/*
+ * 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
+ */
+/*
+ * otg/msc_fd/crc.c - crc table
+ * @(#) balden@belcarra.com|otg/functions/msc/crc.h|20060419204258|03869
+ *
+ * Copyright (c) 2003-2004 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+
+
+extern unsigned int *msc_crc32_table;
+
+#define CRC32_INIT 0xffffffff // Initial FCS value
+#define CRC32_GOOD 0xdebb20e3 // Good final FCS value
+
+#define CRC32_POLY 0xedb88320 // Polynomial for table generation
+
+#define COMPUTE_FCS(val, c) (((val) >> 8) ^ msc_crc32_table[((val) ^ (c)) & 0xff])
+
+int make_crc_table(void);
+void free_crc_table(void);
+unsigned int crc32_compute(unsigned char *src, int len, unsigned int val);
diff --git a/drivers/otg/functions/msc/msc-bo.c b/drivers/otg/functions/msc/msc-bo.c
new file mode 100644
index 000000000000..4d2f35fdd557
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-bo.c
@@ -0,0 +1,177 @@
+/*
+ * 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
+ */
+/*
+ * otg/function/msc/msc-bo.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc-bo.c|20070425221028|52337
+ *
+ * Copyright (c) 2003-2004 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/msc/msc-bo.c
+ * @brief Mass Storage Driver private defines
+ *
+ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
+ *
+ *
+ * @ingroup MSCFunction
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#if defined(LINUX26)
+#include <linux/buffer_head.h>
+#endif
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+
+
+#include "msc-scsi.h"
+#include "msc.h"
+#include "msc-fd.h"
+#include "crc.h"
+
+//#include "rbc.h"
+
+# define CONFIG_OTG_SERIAL_NUMBER_STR "69D38D2036248D3A983C5CDA63888"
+
+/*!
+ * Mass Storage Class - Bulk Only
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+
+u8 msc_index[] = { BULK_OUT, BULK_IN, };
+
+extern struct usbd_function_operations msc_function_ops;
+
+
+
+static struct usbd_endpoint_request msc_if_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_OUT, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, 0, },
+ { BULK_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, PAGE_SIZE, PAGE_SIZE, 0, },
+ { 0, },
+};
+
+
+#ifndef OTG_C99
+
+//#warning "C99"
+static struct usbd_alternate_description msc_data_alternate_descriptions[1];
+
+struct usbd_interface_description msc_interfaces[] = {
+ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ alternate_list:msc_data_alternate_descriptions,},
+};
+
+struct usbd_interface_driver msc_interface_driver;
+/*! msc_global_init - initialize global variables for msc instance
+ * @return void
+ */
+
+void msc_global_init(void){
+
+ ZERO(msc_data_alternate_descriptions[0]);
+
+ msc_data_alternate_descriptions[0].iInterface = CONFIG_OTG_MSC_INTF;
+ msc_data_alternate_descriptions[0].bInterfaceClass = MASS_STORAGE_CLASS;
+ msc_data_alternate_descriptions[0].bInterfaceSubClass = MASS_STORAGE_SUBCLASS_SCSI;
+ msc_data_alternate_descriptions[0].bInterfaceProtocol = MASS_STORAGE_PROTO_BULK_ONLY;
+ msc_data_alternate_descriptions[0].endpoints = 0x02,
+ msc_data_alternate_descriptions[0].endpoint_index = msc_index;
+
+
+
+ ZERO(msc_interface_driver);
+
+ msc_interface_driver.driver.name = "msc-if";
+ msc_interface_driver.driver.fops = &msc_function_ops;
+ msc_interface_driver.interfaces = 0x1;
+ msc_interface_driver.interface_list = msc_interfaces;
+ msc_interface_driver.endpointsRequested = ENDPOINTS;
+ msc_interface_driver.requestedEndpoints = msc_if_endpoint_requests;
+
+}
+
+
+
+#else /* OTG_C99 */
+//#warning "Not C99"
+
+static struct usbd_alternate_description msc_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra Mass Storage - Bulk Only",
+ .bInterfaceClass = MASS_STORAGE_CLASS,
+ .bInterfaceSubClass = MASS_STORAGE_SUBCLASS_SCSI,
+ .bInterfaceProtocol = MASS_STORAGE_PROTO_BULK_ONLY,
+ .endpoints = 0x02,
+ .endpoint_index = msc_index,
+ },
+};
+
+
+
+struct usbd_interface_description msc_interfaces[] = {
+ { alternates:sizeof (msc_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+alternate_list:msc_data_alternate_descriptions,},
+};
+
+
+/*! msc_interface_driver - USB Device Core function driver definition
+ */
+struct usbd_interface_driver msc_interface_driver;
+
+struct usbd_interface_driver msc_interface_driver = {
+ .driver = {
+ .name = "msc-if",
+ .fops = &msc_function_ops, },
+// .driver.name = "msc-if", /*! driver name */
+// .driver.fops = &msc_function_ops, /*!< operations table */
+ .interfaces = 0x1,
+ .interface_list = msc_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = msc_if_endpoint_requests,
+
+ .bFunctionClass = MASS_STORAGE_CLASS,
+ .bFunctionSubClass = MASS_STORAGE_SUBCLASS_SCSI,
+ .bFunctionProtocol = MASS_STORAGE_PROTO_BULK_ONLY,
+ .iFunction = "Belcarra Mass Storage - Bulk Only",
+};
+
+
+
+
+
+
+#endif /* OTG_C99 */
diff --git a/drivers/otg/functions/msc/msc-fd.c b/drivers/otg/functions/msc/msc-fd.c
new file mode 100644
index 000000000000..b8e47d219c48
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-fd.c
@@ -0,0 +1,1568 @@
+/*
+ * 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
+ */
+/*
+ * otg/function/msc/msc.-fd.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc-fd.c|20070711072703|01715
+ *
+ * Copyright (c) 2003-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ * Tony Tang <tt@belcarra.com>
+ *
+ */
+
+
+/*!
+ * @file otg/functions/msc/msc-fd.c
+ * @brief Mass Storage Driver private defines
+ *
+ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
+ *
+ *
+ * Notes:
+ *
+ * 1. Currently we only support the Bulk Only model. Microsoft states that
+ * further support for the mass storage driver will only be done for devices
+ * that conform to the Bulk Only model.
+ *
+ * 2. Multiple LUN's are not supported, but in theory they could be.
+ *
+ * 3. Error handling should be done with STALL but using ZLP seems to also
+ * work. ZLP is usually easier to implement (except possibly on the SA1100.)
+ * We may need to make STALL an option if we find devices (perhaps SA1100)
+ * that cannot reliaby send a ZLP on BULK-IN endpoint.
+ *
+ *
+ * 4. WinXP will match the following:
+ *
+ * USB: Class_08&SubClass_02&Prot_50
+ * USB: Class_08&SubClass_05&Prot_50
+ * USB: Class_08&SubClass_06&Prot_50
+ *
+ * SubClass 02 is MMC or SFF8020I
+ * SubClass 05 is SFF or SFF8070I
+ * SubClass 06 is SCSI
+ *
+ * From the Windows USB Storage FAQ:
+ *
+ * RBC not supported
+ *
+ * SubClass = 6 (SCSI)
+ * CDBs SHOULD NOT be padded to 12 bytes
+ * ModeSense/ModeSelect SHOULD NOT be translated from 1ah/15h to 5ah/55h
+ * should be used for FLASH
+ *
+ * SubClass !=6
+ * CDBs SHOULD be padded to 12 bytes
+ * ModeSense/ModeSelect SHOULD be translated from 1ah/15h to 5ah/55h
+ *
+ * We are using the former, SubClass = 6, and implement the required SCSI operations.
+ *
+ *
+ * TODO
+ *
+ *
+ *
+ * TODO FIXME Bus Interface Notes
+ *
+ * 1. The bus interface driver must correctly implement NAK'ing if not rcv_urb is
+ * present (see au1x00.c or wmmx.c for examples.)
+ *
+ * 2. The bus interface driver must implement USBD_URB_SENDZLP flag (see au1x00.c
+ * for example.)
+ *
+ * @ingroup MSCFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#if defined(LINUX26)
+#include <linux/buffer_head.h>
+#endif
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+
+
+#include "msc-scsi.h"
+#include "msc.h"
+#include "msc-fd.h"
+#include "crc.h"
+
+
+/* Module Parameters ************************************************************************* */
+
+
+#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED
+#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT
+
+#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF
+#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON
+
+#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON
+
+#define DEVICE_PREVENT_REMOVAL 0x0020
+
+
+#define DEF_NUMBER_OF_HEADS 0x10
+#define DEF_SECTORS_PER_TRACK 0x20
+
+
+
+/* MSC ******************************************************************************************** */
+
+int msc_urb_sent (struct usbd_urb *tx_urb, int rc);
+static int msc_recv_urb (struct usbd_urb *urb, int rc);
+
+otg_tag_t msc_fd_trace_tag;
+
+/* Sense Key *********************************************************************************** */
+
+/*!
+ * @brief set_sense_data - set sensedata in msc private struture
+ *
+ * This saves sense data. Sense data indicates what type of error
+ * has occurred and will be returned to the host when a request sense
+ * command is sent.
+
+ * @param function_instance
+ * @param sensedata
+ * @param info
+ * @return none
+ */
+void set_sense_data(struct usbd_function_instance *function_instance, u32 sensedata, u32 info)
+{
+ struct msc_private *msc = function_instance->privdata;
+ TRACE_SENSE(sensedata, info);
+ msc->sensedata = sensedata;
+ msc->info = info;
+}
+
+/* Check blockdev ****************************************************************************** */
+
+/*!
+ * @brief msc_check_blockdev_name - check current status of the block device
+ *
+ * Check if the block device is operational, either generate a failed CSW
+ * or a ZLP if not ready.
+ *
+ * Returns non-zero if the block device is not available for I/O operations
+ * and a failed CSW has already been sent.
+ * @param function_instance
+ * @param zlp
+ * @param name
+ * @return int
+ */
+int msc_check_blockdev_name(struct usbd_function_instance *function_instance, int zlp, char *name)
+{
+ struct msc_private *msc = function_instance->privdata;
+ if (msc->block_dev_state & DEVICE_EJECTED) {
+ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_EJECTED");
+ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
+ (function_instance, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
+ return -EINVAL;
+ }
+
+ if (msc->block_dev_state & DEVICE_CHANGE_ON) {
+ msc->block_dev_state &= ~DEVICE_CHANGE_ON;
+ TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_CHANGE_ON");
+
+ ((SENDZLP == zlp) ? msc_dispatch_query_urb_zlp : msc_start_sending_csw_failed)
+ (function_instance, SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE, msc->lba, USB_MSC_FAILED);
+ return -EINVAL;
+ }
+ //TRACE_MSG0(MSC,"CHECK BLOCKDEV DEVICE_INSERTED");
+ return 0;
+}
+
+/* Generic start recv urb and send csw ********************************************************* */
+
+/*!
+ * @brief msc_start_recv - queue a receive urb
+ *
+ * Ensure that size is a multiple of the endpoint packetsize.
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ * @param function_instance
+ * @param size
+ * @return int
+ */
+int msc_start_recv_urb(struct usbd_function_instance *function_instance, int size)
+{
+ struct usbd_urb *rcv_urb = NULL;
+ struct msc_private *msc = function_instance->privdata;
+ int wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function_instance, BULK_OUT, usbd_high_speed(function_instance));
+ TRACE_MSG0(MSC,"enter msc_start_recv_urb");
+ if ((size % wMaxPacketSize))
+ size = ((size + wMaxPacketSize) / wMaxPacketSize) * wMaxPacketSize;
+ RETURN_EINVAL_UNLESS((rcv_urb = usbd_alloc_urb (function_instance, BULK_OUT, size, msc_recv_urb)));
+ rcv_urb->function_privdata = function_instance;
+ msc->rcv_urb_finished = NULL;
+ RETURN_ZERO_UNLESS(usbd_start_out_urb(rcv_urb));
+ TRACE_MSG0(MSC,"START RECV URB ERROR");
+ usbd_free_urb(rcv_urb);
+ return -EINVAL;
+}
+
+/*!
+ * @brief msc_start_sending_csw - start sending a new data or csw urb
+ *
+ * Generate a CSW (Command Status Wrapper) to send to the the host.
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ * @param function_instance
+ * @param status
+ * @return int
+ */
+int msc_start_sending_csw(struct usbd_function_instance *function_instance, u8 status)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_command_status_wrapper *csw;
+ int length = sizeof(struct msc_command_status_wrapper);
+ struct usbd_urb *tx_urb;
+
+ //TRACE_MSG1(MSC,"START SENDING CSW %08x", status);
+
+ msc->command_state = MSC_STATUS;
+
+ RETURN_EINVAL_UNLESS((tx_urb = usbd_alloc_urb (function_instance, BULK_IN, length, msc_urb_sent)));
+
+ tx_urb->actual_length = length;
+ tx_urb->function_privdata = function_instance;
+
+ // fill in CSW and queue the urb
+ csw = (struct msc_command_status_wrapper *) tx_urb->buffer;
+ csw->dCSWSignature = CSW_SIGNATURE;
+ csw->dCSWTag = msc->command.dCBWTag;
+ csw->dCSWDataResidue = abs(msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes);
+ csw->bCSWStatus = status;
+
+ TRACE_MSG2(MSC,"START SENDING CSW status: %02x data residue: %d", status, csw->dCSWDataResidue);
+
+ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
+ TRACE_MSG0(MSC,"START SENDING CSW FAILED");
+ usbd_free_urb (tx_urb);
+ return -EINVAL;
+}
+
+/*!
+ * @brief msc_start_sending_csw_failed - starting sending a CSW showing failure
+ *
+ * Sets sensedata and generates a CSW with status set to USB_MSC_FAILED.
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ * @param function_instance
+ * @param sensedata
+ * @param info
+ * @param status
+ * @return int
+ */
+int msc_start_sending_csw_failed(struct usbd_function_instance *function_instance, u32 sensedata, u32 info, int status)
+{
+ struct msc_private *msc = function_instance->privdata;
+ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status);
+ set_sense_data(function_instance, sensedata, info);
+
+ return msc_start_sending_csw(function_instance, status);
+}
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * @brief msc_alloc_urb - allocate an urb for returning a query
+ * @param function_instance
+ * @param length
+ * @return pointer to a urb, Returns NULL if there is an error in the USB layer.
+
+ */
+struct usbd_urb * msc_alloc_urb(struct usbd_function_instance *function_instance, int length)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct usbd_urb *urb;
+
+ THROW_IF(!(urb = usbd_alloc_urb (function_instance, BULK_IN, length, msc_urb_sent)), error);
+ return urb;
+ CATCH(error) {
+ msc->command_state = MSC_READY;
+ return NULL;
+ }
+}
+
+/*!
+ * @brief msc_dispatch_query_urb - dispatch an urb containing query data
+ *
+ * @param function_instance
+ * @param urb
+ * @param length
+ * @param status
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_dispatch_query_urb(struct usbd_function_instance *function_instance, struct usbd_urb *urb, int length, int status)
+{
+ int rc;
+ unsigned long flags;
+ struct msc_private *msc = function_instance->privdata;
+
+ TRACE_MSG2(MSC,"DISPATCH URB len: %d flags: %x", length, urb->flags);
+
+ // save information in msc and urb
+ //
+ urb->function_privdata = function_instance;
+ urb->actual_length = msc->TransferLength_in_bytes = length;
+
+ // dispatch urb
+ local_irq_save(flags);
+ if ((rc = usbd_start_in_urb (urb))) {
+
+ TRACE_MSG0(MSC,"DISPATCH URB FAILED");
+ usbd_free_urb (urb);
+
+ // stall?
+ msc->command_state = MSC_READY;
+ local_irq_restore(flags);
+ return -EINVAL;
+ }
+ msc->command_state = MSC_QUERY;
+ msc->status = status;
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*!
+ * @brief msc_dispatch_query_urb_zlp - send a ZLP
+ *
+ * @param function_instance
+ * @param sensedata
+ * @param info
+ * @param status
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_dispatch_query_urb_zlp(struct usbd_function_instance *function_instance, u32 sensedata, u32 info, int status)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct usbd_urb *urb;
+ TRACE_MSG2(MSC, "sensedata: %x status: %x", sensedata, status);
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, 1)));
+ set_sense_data(function_instance, sensedata, info);
+ urb->flags |= USBD_URB_SENDZLP;
+ return msc_dispatch_query_urb(function_instance, urb, 0, status);
+}
+
+/* READ 10 COMMAND - read and send data to the host ******************************************** */
+extern int msc_scsi_read_10(struct usbd_function_instance *function_instance, char *name, int op);
+extern int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb);
+
+/* WRITE 10 - receive data from host and write to block device ********************************* */
+extern int msc_scsi_write_10(struct usbd_function_instance *function_instance, char *name, int op);
+extern void msc_recv_out_blocks(struct usbd_urb *rcv_urb);
+
+/* SCSI Commands ******************************************************************************* */
+
+/*!
+ * @brief msc_scsi_inquiry - process an inquiry
+ *
+ * Used by:
+ * win2k
+ *
+ * @param function_instance
+ * @param name
+ * @param op
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_inquiry(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_inquiry_command *command = (struct msc_scsi_inquiry_command *)&msc->command.CBWCB;
+ struct msc_scsi_inquiry_data *data;
+ struct usbd_urb *urb;
+ int length = sizeof(struct msc_scsi_inquiry_data);
+ u32 temmp;
+
+ /*
+ * C.f. SPC2 7.3 INQUIRY command
+ * C.f. Table 46 - Standard INQUIRY data format
+ *
+ * C.f. Table 47 - Peripheral Qualifier
+ *
+ * 000b The specified peripheral device type is currently connected to this
+ * logical unit.....
+ * 001b The device server is capable of of supporting the peripheral device
+ * type on this logical unit. However, the physical device is not currently
+ * connected to this logical unit.....
+ * 010b Reserved
+ * 011b The device server is not capable of supporting a physical device on
+ * this logical unit....
+ *
+ */
+
+ TRACE_MSG4(MSC,"INQUIRY EnableVPD: %02x LogicalUnitNumber: %02x PageCode: %02x AllocLen: %02x",
+ command->EnableVPD, command->LogicalUnitNumber, command->PageCode, command->AllocationLength);
+ TRACE_MSG1(MSC, "msc->command.dCBWDataTransferLength:%d", msc->command.dCBWDataTransferLength);
+ // XXX THROW_IF(msc->command_state != MSC_READY, error);
+
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+ data = (struct msc_scsi_inquiry_data *)urb->buffer;
+#if 1
+ data->PeripheralQaulifier=(msc->block_dev_state & DEVICE_EJECTED) + ~(msc->block_dev_state & DEVICE_CHANGE_ON);
+
+#else
+ data->PeripheralQaulifier = msc->block_dev_state & (DEVICE_EJECTED | DEVICE_CHANGE_ON) ? 0x1 : 0;
+#endif
+
+ TRACE_MSG1(MSC,"%d",data->PeripheralQaulifier);
+ data->PeripheralDeviceType = 0x00;
+ data->RMB = 0x1;
+ data->ResponseDataFormat = 0x1;
+ data->AdditionalLength = 0x1f;
+
+ strncpy(data->VendorInformation, CONFIG_OTG_MSC_MANUFACTURER, strlen(CONFIG_OTG_MSC_MANUFACTURER));
+ strncpy(data->ProductIdentification, CONFIG_OTG_MSC_PRODUCT_NAME, strlen(CONFIG_OTG_MSC_PRODUCT_NAME));
+
+ return msc_dispatch_query_urb(function_instance, urb, msc->command.dCBWDataTransferLength, USB_MSC_PASSED);
+}
+
+/*!
+ * @brief msc_scsi_read_format_capacity - process a query
+ *
+ * Used by:
+ * win2k
+ *
+ * @param function_instance
+ * @param name
+ * @param op
+ * @return int Returns non-zero if there is an error in the USB layer.
+
+ */
+int msc_scsi_read_format_capacity(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_read_format_capacity_data *data;
+ struct usbd_urb *urb;
+ int length = sizeof(struct msc_scsi_read_format_capacity_data);
+ u32 block_num = msc->capacity;
+ u32 block_len;
+
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+
+ data = (struct msc_scsi_read_format_capacity_data *) urb->buffer;
+
+ data->CapacityListHeader.CapacityListLength = sizeof(data->CurrentMaximumCapacityDescriptor);
+
+ data->CurrentMaximumCapacityDescriptor.NumberofBlocks = block_num;
+ data->CurrentMaximumCapacityDescriptor.DescriptorCode = 0x03;
+ memcpy(data->CurrentMaximumCapacityDescriptor.BlockLength + 1, &block_len, sizeof(block_len));
+
+ return msc_dispatch_query_urb(function_instance, urb, length, USB_MSC_PASSED);
+}
+
+/*!
+ * @brief msc_read_capacity - process a read_capacity command
+ *
+ * Used by:
+ * win2k
+ *
+* @param function_instance
+ * @param name
+ * @param op
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_read_capacity(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_read_capacity_command *command = (struct msc_scsi_read_capacity_command *)&msc->command.CBWCB;
+ struct msc_scsi_read_capacity_data *data;
+ struct usbd_urb *urb;
+ int length = 8;
+ u32 lba;
+
+ /*
+ * C.f. RBC 5.3
+ */
+ lba = be32_to_cpu(command->LogicalBlockAddress);
+
+ TRACE_MSG1(MSC,"READ CAPACITY LBA: %d", lba);
+
+ if ((command->PMI > 1) || (!command->PMI && lba)) {
+ TRACE_MSG1(MSC,"READ CAPACITY PMI: %d", command->PMI);
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, lba, USB_MSC_FAILED);
+ }
+
+ // alloc urb
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+
+ data = (struct msc_scsi_read_capacity_data *) urb->buffer;
+
+ data->LastLogicalBlockAddress = cpu_to_be32(msc->capacity);
+ data->BlockLengthInBytes = cpu_to_be32(msc->block_size);
+
+ TRACE_MSG2(MSC,"RECV READ CAPACITY lba: %x block_size: %x",
+ be32_to_cpu(data->LastLogicalBlockAddress), be32_to_cpu(data->BlockLengthInBytes));
+
+ return msc_dispatch_query_urb(function_instance, urb, length, USB_MSC_PASSED);
+}
+
+
+/*!
+ * @brief msc_scsi_request_sense - process a request_sense command
+ *
+ * @param function_instance
+ * @param name
+ * @param op
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_request_sense(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_request_sense_command * command = (struct msc_scsi_request_sense_command *)&msc->command.CBWCB;
+ struct msc_scsi_request_sense_data *data;
+
+ /*
+ * C.f. SPC2 7.20 REQUEST SENSE command
+ */
+
+ struct usbd_urb *urb;
+ int length = sizeof(struct msc_scsi_request_sense_data);
+
+ // alloc urb
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+
+ data = (struct msc_scsi_request_sense_data *) urb->buffer;
+ memset(command, 0x0, length);
+ data->ErrorCode = SCSI_ERROR_CURRENT;
+ data->SenseKey = msc->sensedata >> 16;
+ data->AdditionalSenseLength = 0xa; /* XXX is this needed */
+ data->AdditionalSenseCode = msc->sensedata >> 8;
+ data->AdditionalSenseCodeQualifier = msc->sensedata;
+ data->Valid = 1;
+
+ set_sense_data(function_instance, SCSI_SENSEKEY_NO_SENSE, 0);
+
+ return msc_dispatch_query_urb(function_instance, urb, msc->command.dCBWDataTransferLength, USB_MSC_PASSED);
+}
+
+/*!
+ * @brief msc_scsi_mode_sense - process a request_sense command
+ *
+ * Used by:
+ * win2k
+ *
+ * @param function_instance
+ * @param name
+ * @param op
+ * @return int Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_mode_sense(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_mode_sense_command* command = (struct msc_scsi_mode_sense_command*)&msc->command.CBWCB;
+ struct msc_scsi_mode_sense_data *data;
+ int length = sizeof(struct msc_scsi_mode_sense_data);
+
+ struct usbd_urb *urb;
+ u8 *cp;
+
+ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
+ command->DBD, command->PageControl, command->PageCode, 0);
+ length = 8;
+
+ // alloc urb
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+
+ cp = (u8 *) urb->buffer;
+ memset(cp, 0x0, length);
+
+ cp[0] = 0;
+ cp[1] = 0;
+ cp[2] = 0; // 0x80 is writeprotect
+ cp[3] = 0x08;
+ cp[4] = 0;
+ cp[5] = 0;
+ cp[6] = 0;
+ cp[7] = 0;
+
+ return msc_dispatch_query_urb(function_instance, urb, msc->command.dCBWDataTransferLength, USB_MSC_PASSED);
+}
+
+/*! msc_scsi_mode_sense - process a process mode sense command
+ *
+ * Used by:
+ * win2k
+ *
+ * XXX this doesn't work, need to re-implement and add these pages.
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int new_msc_scsi_mode_sense(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+
+ /*
+ * C.f. SPC2 7.8.1 MODE SENSE(6) command
+ */
+
+ static struct msc_read_write_error_recovery_page page_01 = {
+ PageCode:0x01,
+ PageLength:0x0A,
+ ReadRetryCount:0x03,
+ WriteRetryCount:0x80,
+ };
+ static struct msc_flexible_disk_page page_05 = {
+ PageCode:0x05,
+ PageLength:0x1E,
+ TransferRate:__constant_cpu_to_be16(0xFA00),
+ NumberofHeads:0xA0,
+ SectorsperTrack:0x00,
+ DataBytesperSector:__constant_cpu_to_be16(0x0002),
+ NumberofCylinders:__constant_cpu_to_be16(0x0000),
+ MotorOnDelay:0x05,
+ MotorOffDelay:0x1E,
+ MediumRotationRate:__constant_cpu_to_be16(0x6801),
+ };
+ static struct msc_removable_block_access_capabilities_page page_1b = {
+ PageCode:0x1B,
+ PageLength:0x0A,
+ TLUN:0x01,
+ };
+ static struct msc_timer_and_protect_page page_1c = {
+ PageCode:0x1c,
+ PageLength:0x06,
+ InactivityTimeMultiplier:0x0A,
+ };
+
+ struct msc_scsi_mode_sense_command *command = (struct msc_scsi_mode_sense_command *)&msc->command.CBWCB;
+ struct msc_scsi_mode_sense_data *data;
+ struct usbd_urb *urb;
+ int length = sizeof(struct msc_scsi_mode_sense_data);
+
+
+ TRACE_MSG4(MSC,"MODE SENSE dbd: %02x PageControl: %02x PageCode: %02x Alloc: %02x",
+ command->DBD, command->PageControl, command->PageCode, 0);
+
+
+ if (msc->block_dev_state & DEVICE_EJECTED) {
+ u16 sector = htons((unsigned short)msc->block_size);
+ u16 cylinder = 0;
+ page_05.NumberofHeads = 0;
+ page_05.SectorsperTrack = 0;
+ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
+ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
+ }
+ else {
+ u16 sector = htons((unsigned short)msc->block_size);
+ u32 size = DEF_NUMBER_OF_HEADS * DEF_SECTORS_PER_TRACK * sector;
+ u16 cylinder = htons(msc->capacity / size);
+ page_05.NumberofHeads = DEF_NUMBER_OF_HEADS;
+ page_05.SectorsperTrack = DEF_SECTORS_PER_TRACK;
+ memcpy(&page_05.DataBytesperSector, &sector, sizeof(sector));
+ memcpy(&page_05.NumberofCylinders, &cylinder, sizeof(cylinder));
+ }
+
+ if (SCSI_MODEPAGE_CONTROL_CURRENT != command->PageControl) {
+ TRACE_MSG2(MSC,"MODE SENSE - requeested other than CONTROL_CURRENT: %d %d",
+ command->PageControl, command->PageCode);
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
+ }
+
+ RETURN_EINVAL_IF(!(urb = msc_alloc_urb(function_instance, length)));
+ data = (struct msc_scsi_mode_sense_data *) urb->buffer;
+
+ data->ModeParameterHeader.WriteProtect = msc->block_dev_state & DEVICE_WRITE_PROTECTED ? 1 : 0;
+
+ switch (command->PageCode) {
+ case SCSI_MODEPAGE_ERROR_RECOVERY:
+ TRACE_MSG0(MSC, "MODEPAGE ERROR_RECOVERY");
+ length = sizeof(struct msc_mode_parameter_header) + sizeof(page_01);
+ data->ModeParameterHeader.ModeDataLength = length - 1;
+ memcpy(&data->ModePages, &page_01, sizeof(page_01));
+ break;
+ case SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE:
+ TRACE_MSG0(MSC, "MODEPAGE struct msc_flexible_disk_page");
+ length = sizeof(struct msc_mode_parameter_header) + sizeof(page_05);
+ data->ModeParameterHeader.ModeDataLength = length - 1;
+ memcpy(&data->ModePages, &page_05, sizeof(page_05));
+ break;
+ case SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS:
+ TRACE_MSG0(MSC, "MODEPAGE REMOVABLE_BLOCK_ACCESS");
+ length = sizeof(struct msc_mode_parameter_header) + sizeof(page_1b);
+ data->ModeParameterHeader.ModeDataLength = length - 1;
+ memcpy(&data->ModePages, &page_1b, sizeof(page_1b));
+ break;
+ case SCSI_MODEPAGE_INFORMATION_EXCEPTIONS:
+ TRACE_MSG0(MSC, "MODEPAGE INFORMATION_EXCEPTIONS");
+ length = sizeof(struct msc_mode_parameter_header) + sizeof(page_1c);
+ data->ModeParameterHeader.ModeDataLength = length - 1;
+ memcpy(&data->ModePages, &page_1c, sizeof(page_1c));
+ break;
+ case SCSI_MODEPAGE_ALL_SUPPORTED:
+ TRACE_MSG0(MSC, "MODEPAGE ALL_SUPPORTED");
+ length = sizeof(struct msc_mode_parameter_header) + sizeof(struct msc_mode_all_pages);
+ data->ModeParameterHeader.ModeDataLength = length - 1;
+ memcpy(&data->ModePages.ModeAllPages.ReadWriteErrorRecoveryPage, &page_01, sizeof(page_01));
+ memcpy(&data->ModePages.ModeAllPages.FlexibleDiskPage, &page_05, sizeof(page_05));
+ memcpy(&data->ModePages.ModeAllPages.RemovableBlockAccessCapabilitiesPage, &page_1b, sizeof(page_1b));
+ memcpy(&data->ModePages.ModeAllPages.TimerAndProtectPage, &page_1c, sizeof(page_1c));
+ break;
+ case SCSI_MODEPAGE_CACHING:
+ // see file_storage.c for an example if we want to support this
+ TRACE_MSG0(MSC, "MODEPAGE CACHING (not supported)");
+ break;
+ }
+
+ TRACE_MSG2(MSC,"LENGTH: %d %d", msc->command.dCBWDataTransferLength, length);
+
+ /*
+ * XXX verify that length is <= 256 bytes, return CHECK_CONDITION if it is
+ */
+ length = MIN(msc->command.dCBWDataTransferLength, length);
+
+ return msc_dispatch_query_urb(function_instance, urb, length, USB_MSC_PASSED);
+}
+
+
+/*! msc_scsi_test_unit_ready - process a test uint ready command
+ *
+ * Used by:
+ * win2k
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_test_unit_ready(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+}
+
+
+/*! msc_scsi_prevent_allow - process a prevent/allow command
+ *
+ * Used by:
+ * win2k
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_prevent_allow(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_prevent_allow_media_removal_command* command =
+ (struct msc_scsi_prevent_allow_media_removal_command*)&msc->command.CBWCB;
+
+ /*
+ * C.f. SPC2 7.12 Table 78 PREVENT ALLOW MEDIA REMOVAL Prevent Field
+ *
+ * 00b Medium removal shall be allowed from both the data transport
+ * element and the attached medium changer (if any).
+ * 01b Medium removal shall be prohibited from the data transport
+ * element but allowed from the attached medium changer (if any).
+ * 10b Medium removal shall be allowed for the data transport element
+ * but prohibited for the attached medium changer.
+ * 11b Medium remval shall be prohibited from both the data transport
+ * element and the attached medium changer
+ *
+ * Prevention shall terminate after 00b or 10b, after a SYNC CACHE or hard reset.
+ */
+
+ // XXX TODO
+ // this is from storageproto.c, shouldn't we implement something?
+ if (command->Prevent)
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
+
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+}
+
+
+/*! msc_scsi_start_stop - process a start/stop command
+ *
+ * C.f. RBC 5.4 and 5.4.2
+ *
+ * Used by:
+ * win2k
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_start_stop(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_start_stop_command* command = (struct msc_scsi_start_stop_command*)&msc->command.CBWCB;
+
+ TRACE_MSG4(MSC,"START STOP: Immed: %d Power: %x LoEj: %d Start: %d",
+ command->IMMED, command->PowerConditions, command->LoEj, command->Start);
+ /*
+ * C.f. 5.4
+ *
+ * IMMED - if set return status immediately after command validation, otherwise
+ * return status as soon operation is completed.
+ *
+ * C.f. 5.4.1 Table 8 POWER CONDITIONS
+ *
+ * 0 - M - no change in power condition
+ * 1 - M - place device in active condition
+ * 2 - M - place device in idle condition
+ * 3 - M - place device in Standby condition
+ * 4 - - reserved
+ * 5 - M - place device in Sleep condition
+ * 6 - - reserved
+ * 7 - 0 - Device Control
+ *
+ * C.f. 5.4.2 Table 9 START STOP control bit definitions
+ *
+ * Power Load/Eject Start
+ * 1-7 x x LoEj and Start Ignored
+ * 0 0 0 Stop the medium
+ * 0 0 1 Make the medium ready
+ * 0 1 0 Unload the medium
+ * 0 1 1 Load the medium
+ *
+ */
+ // XXX TODO
+ // this is from storageproto.c, shouldn't we implement something?
+
+ if (command->Start && command->LoEj)
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_FIELD_IN_CDB, msc->lba, USB_MSC_FAILED);
+
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+}
+
+/*! msc_scsi_verify - process a verify command
+ *
+ * Used by:
+ * win2k
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_verify(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_verify_command *command = (struct msc_scsi_verify_command *)&msc->command.CBWCB;
+
+ /*
+ * C.f. RBC 5.7 VERIFY command
+ */
+ // XXX This actually should use the read_10 function and when
+ // finished reading simply send the following
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+}
+
+
+/*! msc_scsi_mode_select - process a select command
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_mode_select(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ //SCSI_MODE_SELECT_COMMAND *command = (SCSI_MODE_SELECT_COMMAND *)&msc->command.CBWCB;
+
+ /*
+ * C.f. SPC2 7.6 MODE SELECT(6) command
+ */
+
+ // if less than correct amount of data return USB_MSC_PHASE_ERROR - see MV
+ //
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+}
+
+/*! msc_private_pcs - process a private command
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_cmd_private_pcs(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
+}
+
+/*! msc_cmd_unknown - process an unknown command
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_cmd_unknown(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
+}
+
+/*! @struct rbc_dispatch msc-fd.c "otg/functions/msc/msc-fd.c"
+ * @brief encapsulation for rbc command
+ */
+struct rbc_dispatch {
+ u8 op;
+ char *name;
+ int (*rbc_command) (struct usbd_function_instance *, char *, int op);
+ int device_check;
+ u8 dataphase;
+};
+
+/*! Command cross reference
+ *
+ * This is the list of commands observed from each host OS. It is necessarily
+ * incomplete in that we not have reached some condition necessary to have
+ * other commands used.
+ * Win2k WinXP
+ * SCSI_TEST_UNIT_READY yes yes
+ * SCSI_READ_10 yes yes
+ * SCSI_WRITE_10 yes yes
+ * SCSI_READ_CAPACITY yes yes
+ * SCSI_VERIFY yes
+ * SCSI_INQUIRY yes yes
+ * SCSI_MODE_SENSE yes
+ * SCSI_READ_FORMAT_CAPACITY yes yes
+ * SCSI_REQUEST_SENSE
+ * SCSI_PREVENT_ALLOW_MEDIA_REMOVAL
+ * SCSI_START_STOP
+ * SCSI_MODE_SELECT
+ * SCSI_FORMAT_UNIT
+ *
+ */
+
+struct rbc_dispatch rbc_dispatch_table[] = {
+ { SCSI_TEST_UNIT_READY, "SCSI_TEST_UNIT_READY", msc_scsi_test_unit_ready, NOZLP, NODATAPHASE },
+ { SCSI_READ_10, "SCSI_READ_10", msc_scsi_read_10, SENDZLP, HASDATAPHASE },
+ { SCSI_WRITE_10, "SCSI_WRITE_10", msc_scsi_write_10, NOZLP, HASDATAPHASE },
+ { SCSI_READ_CAPACITY, "SCSI_READ_CAPACITY", msc_scsi_read_capacity, SENDZLP, HASDATAPHASE },
+ { SCSI_VERIFY, "SCSI_VERIFY", msc_scsi_verify, NOCHK, NODATAPHASE },
+ { SCSI_INQUIRY, "SCSI_INQUIRY", msc_scsi_inquiry, NOCHK, HASDATAPHASE },
+ { SCSI_MODE_SENSE, "SCSI_MODE_SENSE", msc_scsi_mode_sense, NOCHK, HASDATAPHASE },
+ { SCSI_READ_FORMAT_CAPACITY, "SCSI_READ_FORMAT_CAPACITY", msc_scsi_read_format_capacity, SENDZLP, HASDATAPHASE },
+ { SCSI_REQUEST_SENSE, "SCSI_REQUEST_SENSE", msc_scsi_request_sense, NOCHK, HASDATAPHASE},
+ { SCSI_PREVENT_ALLOW_MEDIA_REMOVAL, "SCSI_PREVENT_ALLOW_MEDIA_REMOVAL", msc_scsi_prevent_allow, NOZLP, NODATAPHASE },
+ { SCSI_START_STOP, "SCSI_START_STOP", msc_scsi_start_stop, NOZLP, NODATAPHASE },
+ { SCSI_MODE_SELECT, "SCSI_MODE_SELECT", msc_scsi_mode_select, NOCHK , NODATAPHASE },
+ { SCSI_FORMAT_UNIT, "SCSI_FORMAT_UNIT", msc_cmd_unknown, NOCHK , NODATAPHASE},
+
+ { SCSI_READ_6, "SCSI_READ_6", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_WRITE_6, "SCSI_WRITE_6", msc_cmd_unknown, NOCHK, NODATAPHASE},
+ { SCSI_RESERVE, "SCSI_RESERVE", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_RELEASE, "SCSI_RELEASE", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_SEND_DIAGNOSTIC, "SCSI_SEND_DIAGNOSTIC", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_SYNCHRONIZE_CACHE, "SCSI_SYNCHRONIZE_CACHE", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_MODE_SENSE_10, "SCSI_MODE_SENSE_10", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_REZERO_UNIT, "SCSI_REZERO_UNIT", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_SEEK_10, "SCSI_SEEK_10", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_WRITE_AND_VERIFY, "SCSI_WRITE_AND_VERIFY", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_WRITE_12, "SCSI_WRITE_12", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_READ_12, "SCSI_READ_12", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_MODE_SELECT_10, "SCSI_MODE_SELECT_10", msc_cmd_unknown, NOCHK, NODATAPHASE },
+ { SCSI_PRIVATE_PCS, "SCSI_PRIVATE_PCS", msc_cmd_private_pcs, NOCHK, NODATAPHASE },
+
+ { 0xff, "SCSI_UNKNOWN", msc_cmd_unknown, NOCHK, NODATAPHASE },
+};
+
+
+
+/*! msc_recv_command - process a new CBW
+ *
+ * Return non-zero if urb was not disposed of.
+ */
+void msc_recv_command(struct usbd_urb *urb, struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_command_block_wrapper *command = (struct msc_command_block_wrapper *)urb->buffer;
+ u8 op = command->CBWCB[0];
+ struct rbc_dispatch *dispatch;
+
+ /*
+ * c.f. section 6.2 - Valid and Meaningful CBW
+ * c.f. section 6.2.1 - Valid CBW
+ *
+ * The CBW was received after the device had sent a CSW or after a
+ * reset XXX check that we only set MSC_READY after reset or sending
+ * CSW.
+ *
+ * The CBW is 31 (1Fh) bytes in length and the bCBWSignature is
+ * equal to 43425355h.
+ */
+ TRACE_MSG1(MSC,"urb->actual_length:%d", urb->actual_length);
+ THROW_IF(31 != urb->actual_length, error);
+ TRACE_MSG3(MSC,"le32_to_cpu(command->dCBWSignature):%08X , le32_to_cpu(command->dCBWTag):%08X, le32_to_cpu(command->dCBWDataTransferLength):%08X", le32_to_cpu(command->dCBWSignature), le32_to_cpu(command->dCBWTag), le32_to_cpu(command->dCBWDataTransferLength));
+
+ THROW_IF(CBW_SIGNATURE != le32_to_cpu(command->dCBWSignature), error);
+
+ /*
+ * c.f. section 6.2.2 - Meaningful CBW
+ *
+ * no reserved bits are set
+ * the bCBWLUN contains a valid LUN supported by the device
+ * both bCBWCBlength and the content of the CBWCB are in accordance with bInterfaceSubClass
+ */
+
+ // XXX checklun etc
+
+ /*
+ * Success
+ */
+ memcpy(&msc->command, command, sizeof(struct msc_command_block_wrapper));
+ msc->data_transferred_in_bytes = msc->TransferLength_in_blocks = msc->TransferLength_in_bytes = 0;
+
+ TRACE_TAG(command->dCBWTag, urb->framenum);
+ usbd_free_urb(urb);
+ urb = NULL;
+
+ /*
+ * Search using the opcode to find the dispatch function to use and
+ * call it.
+ */
+ for (dispatch = rbc_dispatch_table; dispatch->op != 0xff; dispatch++) {
+ CONTINUE_UNLESS ((dispatch->op == op));
+
+
+ //Case , Device no data
+ if(dispatch->dataphase == NODATAPHASE){
+ /*Case 9 case 4 : If Host Expect data, while device has no data, then report phase error directly */
+
+ if (msc->command.dCBWDataTransferLength>0) {
+ if (msc->command.bmCBWFlags & 0x80)
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ else
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+
+ msc->data_transferred_in_bytes = msc->command.dCBWDataTransferLength;
+ msc->command_state = MSC_CBW_PASSED; /* XXX always Passed???*/
+ return;
+ }
+
+ }
+ TRACE_CBW(dispatch->name, dispatch->op, dispatch->device_check);
+ TRACE_RECV(MSC, 8, &(command->CBWCB[1]));
+
+ /* Depending on the command we may need to check if the device is available
+ * and either fail or send a ZLP if it is not
+ */
+ if (dispatch->device_check)
+ RETURN_IF (msc_check_blockdev_name(function_instance, dispatch->device_check, dispatch->name));
+
+ /* Call the specific function that implements the specified command
+ */
+ if (dispatch->rbc_command(function_instance, dispatch->name, op))
+ TRACE_MSG0(MSC,"COMMAND ERROR");
+ return;
+ }
+
+ /* FALL THROUGH if no match is found */
+
+ CATCH(error) {
+ TRACE_MSG0(MSC,"RECV CBW ERROR");
+ if (urb)
+ usbd_free_urb(urb);
+
+ /* XXX which of these do we stall?
+ */
+
+ TRACE_MSG0(MSC,"sos88 halt endpoint");
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->endpoint_state=3;
+ msc->command_state=MSC_WAITFOR_RESET;
+ }
+ //msc_cmd_unknown(msc, "CMD_UNKNOWN", op);
+}
+
+
+/* Sent Function - process a sent urb ********************************************************** */
+
+/*! msc_urb_sent - called to indicate URB transmit finished
+ * @param tx_urb: pointer to struct usbd_urb
+ * @param rc: result
+ *
+ * This is called when an urb is sent. Depending on current state
+ * it may:
+ *
+ * - continue sending data
+ * - send a CSW
+ * - start a recv for a CBW
+ *
+ * This is called from BOTTOM HALF context.
+ *
+ * @return non-zero if urb was not disposed of.
+ */
+int msc_urb_sent (struct usbd_urb *tx_urb, int rc)
+{
+ struct usbd_function_instance *function_instance = tx_urb->function_privdata;
+ struct msc_private *msc = function_instance->privdata;
+ static unsigned char tempbuf=0;
+
+ RETURN_EINVAL_IF(!(function_instance = tx_urb->function_instance));
+ RETURN_EINVAL_IF(usbd_get_device_status(function_instance) == USBD_CLOSING);
+ RETURN_EINVAL_IF(usbd_get_device_state(function_instance) != STATE_CONFIGURED);
+#if defined(CONFIG_OTG_MSC_PIPES_TEST)
+ memset(tx_urb->buffer,tempbuf++,512);
+ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
+ usbd_free_urb (tx_urb);
+ return 0;
+#else
+
+ switch (msc->command_state) {
+ case MSC_DATA_IN_READ:
+ case MSC_DATA_IN_READ_FINISHED:
+ TRACE_MSG0(MSC,"URB SENT READ");
+ return msc_in_read_10_urb_sent(tx_urb);
+
+ case MSC_QUERY:
+ // finished, send CSW
+ msc->data_transferred_in_bytes=tx_urb->actual_length;
+ TRACE_MSG0(MSC,"URB SENT QUERY");
+ msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ break;
+
+ case MSC_STATUS:
+ default:
+ // sent a CSW need to receive the next CBW
+ TRACE_MSG0(MSC,"URB SENT STATUS");
+ msc->command_state = MSC_READY;
+ msc_start_recv_urb(function_instance, sizeof(struct msc_command_block_wrapper));
+ break;
+ }
+ usbd_free_urb (tx_urb);
+ return 0;
+#endif
+}
+
+
+/* Receive Function - receiving an urb ********************************************************* */
+
+/*! msc_recv_urb - process a received urb
+ *
+ * Return non-zero if urb was not disposed of.
+ */
+static int msc_recv_urb (struct usbd_urb *rcv_urb, int rc)
+{
+ struct usbd_function_instance *function_instance = rcv_urb->function_privdata;
+ struct msc_private *msc = function_instance->privdata;
+
+ RETURN_EINVAL_IF(!msc->connected);
+
+ TRACE_MSG4(MSC, "RECV URB pointer: %08x, length: %d state: %d, rc:%d", rcv_urb, rcv_urb->actual_length, msc->command_state,rc);
+
+ if(rc!=0) return -EINVAL; //XXX if urb callback with error, now do nothing
+
+#if defined(CONFIG_OTG_MSC_PIPES_TEST)
+ printk(KERN_INFO "rcv_urb->actual_length:%d\n",rcv_urb->actual_length);
+ RETURN_ZERO_UNLESS(usbd_start_out_urb(rcv_urb));
+ TRACE_MSG0(MSC,"START RECV URB ERROR");
+ usbd_free_urb(rcv_urb);
+ return -EINVAL;
+#else /*defined(CONFIG_OTG_MSC_PIPES_TEST) */
+
+ switch(msc->command_state) {
+
+ // ready to start a new transaction
+ case MSC_READY:
+ msc_recv_command(rcv_urb, function_instance);
+ return 0;
+
+ // we think we are receiving data
+ case MSC_DATA_OUT_WRITE:
+ case MSC_DATA_OUT_WRITE_FINISHED:
+
+ //if(rcv_urb->actual_length)
+ //printk(KERN_INFO "sos00 rcv_urb->actual_length: %x\n", rcv_urb->actual_length);
+ //else
+ // printk(KERN_INFO "sos00 rcv_urb->actual_length = 0\n");
+
+ msc_recv_out_blocks(rcv_urb);
+ return 0;
+
+ // we think we are sending data
+ case MSC_DATA_IN_READ:
+ case MSC_DATA_IN_READ_FINISHED:
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
+ break;
+
+ // we think we are sending status
+ case MSC_STATUS:
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_INVALID_COMMAND, msc->lba, USB_MSC_FAILED);
+ break;
+ case MSC_WAITFOR_RESET:
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->endpoint_state=3;
+ break;
+
+ // we don't think
+ case MSC_UNKNOWN:
+ default:
+ TRACE_MSG0(MSC,"RECV URB ERROR");
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->endpoint_state=3;
+ msc->command_state=MSC_WAITFOR_RESET;
+ }
+ // let caller dispose of urb
+
+ return -EINVAL;
+#endif /*defined(CONFIG_OTG_MSC_PIPES_TEST)*/
+
+}
+
+/* USB Device Functions ************************************************************************ */
+
+
+#if 0
+static void msc_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ TRACE_MSG0(MSC,"--");
+}
+
+static int msc_set_configuration (struct usbd_function_instance *function, int wValue)
+{
+ TRACE_MSG1(MSC,"wValue: %02x", wValue);
+ return 0;
+}
+
+static int msc_set_interface (struct usbd_function_instance *function, int wIndex, int wValue)
+{
+ TRACE_MSG2(MSC,"wIndex: %02x wValue: %02x", wIndex, wValue);
+ return 0;
+}
+#endif
+
+/* USB Device Functions ************************************************************************ */
+
+/*! msc_device_request - called to indicate urb has been received
+ *
+ * This function is called when a SETUP packet has been received that
+ * should be handled by the function driver. It will not be called to
+ * process the standard chapter nine defined requests.
+ *
+ * Return non-zero for failure.
+ */
+int msc_device_request(struct usbd_function_instance *function_instance, struct usbd_device_request *request)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct usbd_urb *urb;
+
+ u8 bRequest = request->bRequest;
+ u8 bmRequestType = request->bmRequestType;
+ u16 wValue = le16_to_cpu(request->wValue);
+ u16 wIndex = le16_to_cpu(request->wIndex);
+ u16 wLength = le16_to_cpu(request->wLength);
+
+ TRACE_MSG5(MSC, "MSC RECV SETUP bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x",
+ bmRequestType, bRequest, wValue, wIndex, wLength);
+
+ // verify that this is a usb class request per cdc-acm specification or a vendor request.
+ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
+
+ // determine the request direction and process accordingly
+ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_CLASS:
+ switch (request->bRequest) {
+ case MSC_BULKONLY_RESET:
+ TRACE_MSG0(MSC, "MSC_BULKONLY_RESET");
+ RETURN_EINVAL_IF((wValue !=0) | (wIndex !=0) | ( wLength !=0));
+ // set msc back to reset state here
+ //.....
+
+ msc->command_state = MSC_WAITFOR_CLEAR;
+ msc->endpoint_state=3;
+ //////////////////////////
+ return 0;
+ default:
+ TRACE_MSG0(MSC, "UNKNOWN H2D");
+ return -EINVAL;
+
+ }
+
+ case USB_REQ_DEVICE2HOST | USB_REQ_TYPE_CLASS:
+ switch (request->bRequest) {
+ case MSC_BULKONLY_GETMAXLUN:
+ RETURN_EINVAL_IF((wValue !=0) | (wIndex !=0) | ( wLength !=1));
+ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function_instance, 1, NULL)));
+ urb->buffer[0] = 0;
+ urb->actual_length = 1;
+ RETURN_ZERO_IF(!usbd_start_in_urb(urb));
+ usbd_free_urb(urb);
+ return -EINVAL;
+ default:
+ TRACE_MSG0(MSC, "UNKNOWN D2H");
+ return -EINVAL;
+ }
+ default:
+ TRACE_MSG0(MSC,"unknown MSC device request");
+ return -EINVAL;
+ break;
+ }
+ return -EINVAL;
+}
+
+struct usbd_function_instance *proc_function_instance = NULL;
+
+int msc_os_init_l24(struct usbd_function_instance *function_instance);
+int msc_io_init_l24(struct usbd_function_instance *function_instance);
+void msc_io_exit_l24(void);
+
+/*! msc_function_enable - this is called by the USBD core layer
+ *
+ * This is called to initialize the function when a bus interface driver
+ * is loaded.
+ */
+static int msc_function_enable (struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = NULL;
+
+ RETURN_EINVAL_UNLESS((msc = CKMALLOC(sizeof(struct msc_private))));
+
+ // XXX MODULE LOCK HERE
+
+ function_instance->privdata = msc;
+
+ // XXX TODO need to verify that serial number is minimum of 12
+
+ init_waitqueue_head(&msc->msc_wq);
+ init_waitqueue_head(&msc->ioctl_wq);
+
+ msc->block_dev_state = DEVICE_EJECTED;
+ msc->command_state = MSC_READY;
+ msc->io_state = MSC_INACTIVE;
+ msc->command_state = MSC_READY;
+
+ msc_os_init_l24(function_instance);
+ msc_io_init_l24(function_instance);
+
+ return 0;
+}
+
+extern void msc_close_blockdev (struct usbd_function_instance *function_instance);
+
+/*! msc_function_disable - this is called by the USBD core layer
+ *
+ * This is called to close the function when a bus interface driver
+ * is unloaded.
+ */
+static void msc_function_disable (struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+
+ TRACE_MSG0(MSC,"FUNCTION EXIT");
+
+
+ msc_close_blockdev(function_instance);
+ function_instance->privdata = NULL;
+
+ LKFREE(msc);
+ msc_io_exit_l24();
+ // XXX MODULE UNLOCK HERE
+}
+
+
+/*! msc_set_configuration
+ * @brief - called to set configuration according received urb
+ * @param function_instance
+ * @param configuration
+ * @return int
+ */
+int msc_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct msc_private *msc = function_instance->privdata;
+ struct usbd_urb *tx_urb;
+
+ TRACE_MSG0(MSC, "CONFIGURED");
+
+ // XXX Need to differentiate between non-zero, zero and non-zero done twice
+ usbd_flush_endpoint_index(function_instance,BULK_IN);
+ usbd_flush_endpoint_index(function_instance,BULK_OUT);
+
+ TRACE_MSG0(MSC,"EVENT CONFIGURED");
+ msc->endpoint_state=0;
+ msc->connected = 1;
+ msc->command_state = MSC_READY;
+#if defined(CONFIG_OTG_MSC_PIPES_TEST)
+
+ msc_start_recv_urb(function_instance, sizeof(struct msc_command_block_wrapper));
+
+ RETURN_EINVAL_UNLESS((tx_urb = usbd_alloc_urb (function_instance, BULK_IN, 512, msc_urb_sent)));
+
+ tx_urb->actual_length = 512;
+ tx_urb->function_privdata = function_instance;
+ memset(tx_urb->buffer,0x66,512);
+ RETURN_ZERO_UNLESS(usbd_start_in_urb (tx_urb));
+ usbd_free_urb (tx_urb);
+ return -EINVAL;
+#else /*defined(CONFIG_OTG_MSC_PIPES_TEST) */
+ msc_start_recv_urb(function_instance, 512);
+#endif
+ #if 0
+ local_irq_save(flags);
+ if (msc->io_state & MSC_IOCTL_WAITING) {
+ msc->io_state &= ~MSC_IOCTL_WAITING;
+ TRACE_MSG0(MSC, "WAKEUP");
+ }
+ local_irq_restore(flags);
+ #endif
+ wake_up_interruptible(&msc->ioctl_wq);
+
+ return 0;
+}
+
+/*! int msc_reset ( struct usbd_function_instance * )
+ * @brief - called to process reset (urb) command
+ * @param function_instance
+ * @return int
+ */
+int msc_reset (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct msc_private *msc = function_instance ? function_instance->privdata : NULL;
+ int connected = msc ? msc->connected : 0;
+
+ TRACE_MSG1(MSC,"msc: %x", (int)msc);
+
+
+ TRACE_MSG2(MSC,"EVENT RESET: connected %d msc->io_state %d", msc->connected, msc->io_state);
+
+ #if 0
+ local_irq_save(flags);
+ if (msc->io_state & MSC_IOCTL_WAITING) {
+ msc->io_state &= ~MSC_IOCTL_WAITING;
+ TRACE_MSG0(MSC, "WAKEUP");
+ wake_up_interruptible(&msc->ioctl_wq);
+ }
+ local_irq_restore(flags);
+ #endif
+ if (msc->connected ==1) wake_up_interruptible(&msc->ioctl_wq);
+ msc->connected = 0;
+ RETURN_ZERO_UNLESS(connected);
+
+ // XXX we should have a semaphore to protect this
+ RETURN_ZERO_UNLESS (msc->rcv_urb_finished);
+ usbd_free_urb (msc->rcv_urb_finished);
+ msc->rcv_urb_finished = NULL;
+ msc->io_state=0;
+ return 0;
+}
+
+/*! msc_suspended
+ * @brief- called to process msc suspended command
+ * @param function_instance
+ * @return int
+ */
+int msc_suspended (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct msc_private *msc = function_instance->privdata;
+
+ TRACE_MSG1(MSC,"msc: %x", (int)msc);
+ if (msc->connected ==1) wake_up_interruptible(&msc->ioctl_wq);
+ msc->connected =0;
+ TRACE_MSG0(MSC, "SUSPENDED");
+ return 0;
+}
+
+
+/*!int msc_resumed (struct usbd_function_instance * )
+ * @brief msc_resumed - called to process msc resumed command
+ * @param function_instance
+ * @return int
+ */
+int msc_resumed (struct usbd_function_instance *function_instance)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct msc_private *msc = function_instance->privdata;
+ TRACE_MSG1(MSC,"msc: %x", (int)msc);
+ if (msc->connected ==0) wake_up_interruptible(&msc->ioctl_wq);
+ msc->connected =1;
+ TRACE_MSG0(MSC, "RESUMED");
+
+ return 0;
+}
+
+/*!
+ * @brief msc_endpoint_cleared- called to indicate endpoint has been cleared
+ * @param function_instance
+ * @param bEndpointAddress
+ * @return none
+ */
+static void msc_endpoint_cleared (struct usbd_function_instance *function_instance, int bEndpointAddress)
+{
+ struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ struct msc_private *msc = function_instance->privdata;
+
+ TRACE_MSG1(MSC,"bEndpointAddress: %02x", bEndpointAddress);
+
+ switch (msc->command_state) {
+
+ case MSC_CBW_PHASE_ERROR:
+ msc_start_sending_csw(function_instance, USB_MSC_PHASE_ERROR);
+
+ break;
+
+ case MSC_CBW_PASSED:
+ msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+
+ break;
+
+ case MSC_WAITFOR_RESET:
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->endpoint_state=BULK_IN_HALTED | BULK_OUT_HALTED;
+ break;
+ case MSC_WAITFOR_CLEAR:
+ if(bEndpointAddress&0x80){
+ msc->endpoint_state &= ~BULK_IN_HALTED;
+ TRACE_MSG0(MSC,"Bulk IN cleared");
+ }
+ else{
+ msc->endpoint_state &= ~BULK_OUT_HALTED;
+ TRACE_MSG0(MSC,"Bulk OUT cleared");
+ }
+
+ if(!msc->endpoint_state){
+ msc->command_state = MSC_READY;
+ msc_start_recv_urb(function_instance, sizeof(struct msc_command_block_wrapper));
+
+
+ TRACE_MSG0(MSC,"Ready to receive a new CBW");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
+
+
+/* ********************************************************************************************* */
+/*! msc_function_ops usbd_function_option structure variable, initialized for all usbd core called operations
+ */
+struct usbd_function_operations msc_function_ops = {
+ device_request: msc_device_request,
+ function_enable: msc_function_enable,
+ function_disable: msc_function_disable,
+ set_configuration: msc_set_configuration,
+ reset: msc_reset,
+ suspended: msc_suspended,
+ resumed: msc_resumed,
+ endpoint_cleared: msc_endpoint_cleared,
+};
+
+/* ********************************************************************************************* */
diff --git a/drivers/otg/functions/msc/msc-fd.h b/drivers/otg/functions/msc/msc-fd.h
new file mode 100644
index 000000000000..e31a17041024
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-fd.h
@@ -0,0 +1,83 @@
+/*
+ * 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
+ */
+/*
+ * otg/msc_fd/msc.h - Mass Storage Class
+ * @(#) sl@belcarra.com|otg/functions/msc/msc-fd.h|20060702220757|39666
+ *
+ * Copyright (c) 2003-2004 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+#ifndef MSC_FD_H
+#define MSC_FD_H 1
+
+extern int msc_dispatch_query_urb(struct usbd_function_instance *, struct usbd_urb *, int , int );
+extern int msc_start_sending_csw(struct usbd_function_instance *, u8 );
+extern int msc_dispatch_query_urb_zlp(struct usbd_function_instance *, u32 sensedata, u32 info, int status);
+extern int msc_start_sending_csw_failed(struct usbd_function_instance *, u32 sensedata, u32 info, int status);
+extern int msc_start_recv_urb(struct usbd_function_instance *, int size);
+
+#define NOCHK 0
+#define NOZLP 1
+#define SENDZLP 2
+
+#if 1
+/*! @name MSC activity trace functions */
+/*! @{*/
+
+static __inline__ void TRACE_SENSE(unsigned int sense, unsigned int info)
+{
+ TRACE_MSG2(MSC, "--> SENSE: %06x INFO: %08x", sense, info);
+}
+static __inline__ void TRACE_RLBA(unsigned int lba, unsigned int crc)
+{
+ TRACE_MSG2(MSC, "<-- rlba [%8x %08x]", lba, crc);
+}
+static __inline__ void TRACE_SLBA(unsigned int lba, unsigned int crc)
+{
+ TRACE_MSG2(MSC, "--> slba [%8x %08x]", lba, crc);
+}
+static __inline__ void TRACE_TLBA(unsigned int lba, unsigned int crc)
+{
+ TRACE_MSG2(MSC, "--> tlba [%8x %08x]", lba, crc);
+}
+static __inline__ void TRACE_TAG(unsigned int tag, unsigned int frame)
+{
+ TRACE_MSG2(MSC, "--> TAG: %8x FRAME: %03x", tag, frame);
+}
+static __inline__ void TRACE_CBW(char *msg, int val, int check)
+{
+ TRACE_MSG3(MSC, " --> %s %02x check: %d", msg, val, check);
+}
+//static __inline__ void TRACE_RECV(unsigned char *cp)
+//{
+// TRACE_MSG8(MSC, "<-- recv [%02x %02x %02x %02x %02x %02x %02x %02x]",
+// cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+//}
+//static __inline__ void TRACE_SENT(unsigned char *cp)
+//{
+// TRACE_MSG8(MSC, "--> sent [%02x %02x %02x %02x %02x %02x %02x %02x]",
+// cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+//}
+
+/*! @} */
+
+#endif
+
+
+#endif /* MSC_H */
diff --git a/drivers/otg/functions/msc/msc-io-l24.c b/drivers/otg/functions/msc/msc-io-l24.c
new file mode 100644
index 000000000000..cf995b1b7ca1
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-io-l24.c
@@ -0,0 +1,325 @@
+/*
+ * 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
+ */
+/*
+ * otg/function/msc/msc-io-l24.c - MSC IO
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc-io-l24.c|20070425221028|61154
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Tony Tang <tt@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/msc/msc-io-l24.c
+ * @brief
+ *
+ * NOTES
+ *
+ * TODO
+ *
+ * 1. implement prevent removal command.
+ *
+ * @ingroup MSCFunction
+ * @ingroup LINUXOS
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+#include <linux/poll.h>
+#include <linux/sched.h>
+//#include <linux/devfs_fs_kernel.h>
+
+#include <linux/kdev_t.h>
+#include <linux/blkdev.h>
+
+#include "msc-scsi.h"
+#include "msc.h"
+#include "msc-fd.h"
+#include "crc.h"
+#include "msc-io.h"
+
+extern void msc_open_blockdev (struct usbd_function_instance *function_instance);
+extern void msc_close_blockdev (struct usbd_function_instance *function_instance);
+// XXX no use extern int msc_connection_blockdev (struct usbd_function_instance *function_instance);
+//XXX No use extern struct msc_private msc_private;
+
+#define DEVICE_EJECTED 0x0001 //MEDIA_EJECTED
+#define DEVICE_INSERTED 0x0002 //MEDIA_INSERT
+#define DEVICE_MOUNTED 0x0001 //DEVICE_MOUNTED
+#define DEVICE_UNMOUNTED 0x0002 //DEVICE_UNMOUNTED
+
+#if 0
+void msc_io_wait(struct usbd_function_instance *function_instance, u32 flag)
+{
+ unsigned long flags;
+ msc->io_state |= MSC_IOCTL_WAITING | flag;
+ TRACE_MSG1(MSC, "SLEEPING io_state: %x", msc->io_state);
+ interruptible_sleep_on(&msc->ioctl_wq);
+}
+
+void msc_io_wakup(struct usbd_function_instance *function_instance)
+{
+ unsigned long flags;
+ local_irq_save(flags);
+ if (msc->io_state & MSC_IOCTL_WAITING) {
+ msc->io_state &= ~MSC_IOCTL_WAITING;
+ TRACE_MSG0(MSC, "WAKEUP");
+ wake_up_interruptible(&msc->ioctl_wq);
+ }
+ local_irq_restore(flags);
+}
+#endif
+
+extern struct usbd_function_instance *proc_function_instance;
+
+/*! int msc_io_ioctl_internal (struct inode* , unsigned int, unsigned long)
+ * @brief process io control command
+ *
+ * @param inode
+ * @param cmd - ip command
+ * @param arg - command argument
+ * @return 0 for success
+ *
+ *
+ * XXX Can we get function_instance passed in as arg??? XXX
+ */
+int msc_io_ioctl_internal(struct inode *inode, unsigned int cmd, unsigned long arg)
+{
+ struct usbd_function_instance *function_instance = proc_function_instance; // XXX
+ struct msc_private *msc = function_instance ? function_instance->privdata : NULL;
+ int i;
+ int len;
+ int flag;
+
+ static char func_buf[32];
+ struct otgmsc_mount mount;
+ unsigned long flags;
+
+ TRACE_MSG2(MSC, "cmd: %d connect: %d", cmd, msc->connected);
+ memset(&mount, 0, sizeof(mount));
+ switch (cmd) {
+
+ /* Mount - make the specified major/minof device available for use
+ * by the USB Host, this does not require an active connection.
+ */
+ case OTGMSC_START:
+ TRACE_MSG0(MSC, "Mounting the device");
+ RETURN_EINVAL_IF(copy_from_user(&mount, (void *)arg, _IOC_SIZE(cmd)));
+
+ TRACE_MSG3(MSC, "major=%d minor=%d state=%d", mount.major, mount.minor, msc->block_dev_state);
+ msc->major = mount.major;
+ msc->minor = mount.minor;
+
+ //msc->io_state = MSC_INACTIVE;
+ inode->i_rdev=MKDEV(msc->major, msc->minor);
+ msc_open_blockdev (function_instance);
+ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_READY);
+
+ mount.status = (msc->block_dev_state == DEVICE_INSERTED) ? DEVICE_MOUNTED : DEVICE_UNMOUNTED;
+
+ TRACE_MSG1(MSC, "Device is mounted status: %d", mount.status);
+
+ // XXX Need to copy result back to user space
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
+ TRACE_MSG0(MSC, "Device mounted");
+ return 0;
+
+ /* Umount - make the currently mounted device unavailable to the USB Host,
+ * if there is pending block i/o block until it has finished.
+ * Note that if the driver is unloaded the waiting ioctl process
+ * must be woken up and informed with error code.
+ */
+ case OTGMSC_STOP:
+
+ TRACE_MSG0(MSC, "Unmounting the device");
+
+ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
+
+ if (msc->command_state != MSC_READY) {
+ TRACE_MSG0(MSC, "SLEEPING");
+ interruptible_sleep_on(&msc->ioctl_wq);
+ TRACE_MSG0(MSC, "AWAKE");
+ }
+
+ RETURN_EAGAIN_UNLESS(msc->command_state == MSC_INACTIVE);
+
+ // XXX Need to copy result back to user space
+ msc->major = mount.major;
+ msc->minor = mount.minor;
+ msc_close_blockdev(function_instance);
+ mount.status = DEVICE_UNMOUNTED;
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
+ TRACE_MSG0(MSC, "Device unmounted");
+ return 0;
+
+
+ /* Status - return the current mount status.
+ */
+ case OTGMSC_STATUS:
+ TRACE_MSG0(MSC, "Mount status");
+ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
+ if (msc->block_dev_state == DEVICE_EJECTED)
+ mount.status = DEVICE_UNMOUNTED;
+ else
+ mount.status = DEVICE_MOUNTED;
+
+ // XXX Need to copy result back to user space
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
+ return 0;
+
+ /* Wait_Connect - if not already connected wait until connected,
+ * Note that if the driver is unloaded the waiting ioctl process
+ * must be woken up and informed with error code.
+ */
+ case OTGMSC_WAIT_CONNECT:
+ TRACE_MSG1(MSC, "Wait for connect: connected: %d", msc->connected);
+ // XXX local_irq_save(flags);
+
+ if (msc->connected == 0) {
+ TRACE_MSG0(MSC, "SLEEPING");
+ interruptible_sleep_on (&msc->ioctl_wq);
+ TRACE_MSG0(MSC, "AWAKE");
+ }
+ RETURN_EAGAIN_UNLESS(msc->connected);
+
+ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
+ TRACE_MSG0(MSC, "Device connected");
+ return 0;
+
+ /* Wait_DisConnect - if not already disconnected wait until disconnected,
+ * Note that if the driver is unloaded the waiting ioctl process
+ * must be woken up and informed with error code.
+ */
+ case OTGMSC_WAIT_DISCONNECT:
+ TRACE_MSG1(MSC, "Wait for disconnect: connected: %d", msc->connected);
+
+ if (msc->connected == 1) {
+ TRACE_MSG0(MSC, "SLEEPING");
+ interruptible_sleep_on (&msc->ioctl_wq);
+ TRACE_MSG0(MSC, "AWAKE");
+ }
+ RETURN_EAGAIN_IF(msc->connected);
+ RETURN_EINVAL_IF (copy_from_user (&mount, (void *)arg, _IOC_SIZE(cmd)));
+ RETURN_EINVAL_IF (copy_to_user((void *)arg, &mount, sizeof(mount)));
+ TRACE_MSG0(MSC, "Device disconnected");
+ return 0;
+
+ default:
+ TRACE_MSG1(MSC, "Unknown command: %x", cmd);
+ TRACE_MSG1(MSC, "OTGMSC_START: %x", OTGMSC_START);
+ TRACE_MSG1(MSC, "OTGMSC_WRITEPROTECT: %x", OTGMSC_WRITEPROTECT);
+ TRACE_MSG1(MSC, "OTGMSC_STOP: %x", OTGMSC_STOP);
+ TRACE_MSG1(MSC, "OTGMSC_STATUS: %x", OTGMSC_STATUS);
+ TRACE_MSG1(MSC, "OTGMSC_WAIT_CONNECT: %x", OTGMSC_WAIT_CONNECT);
+ TRACE_MSG1(MSC, "OTGMSC_WAIT_DISCONNECT: %x", OTGMSC_WAIT_DISCONNECT);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+
+/*! int msc_io_ioctl(struct inode* inode, struct file*, unsigned int, unsigned long)
+ * @brief verify ioctl command and call msc_io_ioctl_internal to process ioctl command
+ *
+ * @param inode pointer to device node
+ * @param filp file pointer
+ * @param cmd command to process
+ * @param arg command argument
+ * @return non-zero for error
+ */
+int msc_io_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct usbd_function_instance *function_instance = proc_function_instance; // XXX
+ struct msc_private *msc = function_instance ? function_instance->privdata : NULL;
+ int i;
+ int len;
+ int flag;
+
+ TRACE_MSG6(MSC, "cmd: %08x arg: %08x type: %02d nr: %02d dir: %02d size: %02d",
+ cmd, arg, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));
+
+ RETURN_EINVAL_UNLESS (_IOC_TYPE(cmd) == OTGMSC_MAGIC);
+ RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGMSC_MAXNR);
+
+ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_READ) && !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)));
+ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_WRITE) && !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)));
+
+ return msc_io_ioctl_internal(inode, cmd, arg);
+}
+
+/*! msc_io_proc_ioctl -process ioctl command
+ *
+ * @param inode - device node pointer
+ * @param filp - file pointer
+ * @param cmd - command to process
+ * @param arg - command argument
+ * @return non-zero for error
+ */
+
+int msc_io_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct usbd_function_instance *function_instance = proc_function_instance; // XXX
+ struct msc_private *msc = function_instance ? function_instance->privdata : NULL;
+ return msc_io_ioctl(inode, filp, cmd, arg);
+}
+
+
+static struct file_operations msc_io_proc_switch_functions = {
+ioctl:msc_io_proc_ioctl,
+};
+
+
+/*! int msc_io_init_l24(struct usbd_function_instance* )
+ * @brief - create proc dircetory entry: /proc/msc_io
+ *
+ * @param function_instance - pointer to this function driver instance
+ * @return 0 for success
+ */
+int msc_io_init_l24(struct usbd_function_instance *function_instance)
+{
+ struct proc_dir_entry *message = NULL;
+
+ proc_function_instance = function_instance;
+
+ THROW_IF (!(message = create_proc_entry ("msc_io", 0666, 0)), error);
+ message->proc_fops = &msc_io_proc_switch_functions;
+ CATCH(error) {
+ printk(KERN_ERR"%s: creating /proc/msc_io failed\n", __FUNCTION__);
+ if (message)
+ remove_proc_entry("msc_io", NULL);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*! void msc_io_exit_l24()
+ * @brief -remove and exit proc operation
+ * @return none
+ */
+void msc_io_exit_l24(void)
+{
+ remove_proc_entry("msc_io", NULL);
+ proc_function_instance = NULL;
+}
diff --git a/drivers/otg/functions/msc/msc-io.h b/drivers/otg/functions/msc/msc-io.h
new file mode 100644
index 000000000000..2e1b7cae224d
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-io.h
@@ -0,0 +1,72 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/msc/msc-io.h - Mass Storage Class
+ * @(#) balden@belcarra.com|otg/functions/msc/msc-io.h|20060510202833|21396
+ *
+ * Copyright (c) 2003-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/msc/msc-io.h
+ * @brief Mass Storage Driver private defines
+ *
+ * OTGMSC_START - make specified major/minor device available to USB Host
+ * OTGMSC_WRITEPROTECT - set or reset write protect flag
+ *
+ * OTGMSC_STOP - remove access to current device, block until pending I/O finished
+ * OTGMSC_STATUS - remove current USB connection status (connected or disconnected)
+ * OTGMSC_WAIT_CONNECT - wait until device is connected (may return immediately if already connected)
+ * OTGMSC_WAIT_DISCONNECT - wait until device is disconnected (may return immediately if already disconnected)
+ *
+ * @ingroup MSCFunction
+ */
+
+//#ifndef MSC_H
+//#define MSC_H 1
+
+#define MSC_IO "/proc/msc_io"
+/*! @struct otgmsc_mount msc-io.h otg/functions/msc/msc-io.h
+ * @brief struct for managing device mount/umount operation
+ */
+struct otgmsc_mount {
+ int major;
+ int minor;
+ int lun;
+ int writeprotect;
+ int result;
+ int status;
+};
+
+#define OTGMSC_MAGIC 'M'
+#define OTGMSC_MAXNR 10
+
+#define OTGMSC_START _IOWR(OTGMSC_MAGIC, 1, struct otgmsc_mount)
+#define OTGMSC_WRITEPROTECT _IOWR(OTGMSC_MAGIC, 2, struct otgmsc_mount)
+
+#define OTGMSC_STOP _IOR(OTGMSC_MAGIC, 1, struct otgmsc_mount)
+#define OTGMSC_STATUS _IOR(OTGMSC_MAGIC, 2, struct otgmsc_mount)
+#define OTGMSC_WAIT_CONNECT _IOR(OTGMSC_MAGIC, 3, struct otgmsc_mount)
+#define OTGMSC_WAIT_DISCONNECT _IOR(OTGMSC_MAGIC, 4, struct otgmsc_mount)
+
+
+#define MSC_CONNECTED 0x01
+#define MSC_DISCONNECTED 0x02
+#define MSC_WRITEPROTECTED 0x04
+
+//#endif /* MSC_H */
diff --git a/drivers/otg/functions/msc/msc-linux.c b/drivers/otg/functions/msc/msc-linux.c
new file mode 100644
index 000000000000..f8b0170bf0a3
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-linux.c
@@ -0,0 +1,1568 @@
+/*
+ * 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
+ */
+/*
+ * otg/function/msc/msc-linux.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc-linux.c|20070425221028|41479
+ *
+ * Copyright (c) 2003-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Tony Tang <tt@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+
+/*!
+ * @file otg/functions/msc/msc-linux.c
+ * @brief Mass Storage Driver private defines
+ *
+ *
+ * This is a Mass Storage Class Function that uses the Bulk Only protocol.
+ *
+ * To use simply load with something like:
+ *
+ * insmod msc_fd.o vendor_id=0xffff product_id=0xffff
+ *
+ * Notes:
+ *
+ * 1. Currently block I/O is done a page at a time. I have not determined if
+ * it is possible to dispatch a multiple page generic request. It should at
+ * least be possible to queue page requests.
+ *
+ * 2. Currently for READ operations we have a maximum of one outstanding
+ * read block I/O and send urb. These are allowed to overlap so that we can
+ * continue to read data while sending data to the host.
+ *
+ * 3. Currently for WRITE operations we have a maximum of one outstanding
+ * recv urb and one outstanding write block I/O. These are allowed to
+ * overlap so that we can continue to receive data from the host while
+ * waiting for writing to complete.
+ *
+ * 4. It would be possible to allow multiple writes to be pending, to the
+ * limit of the page cache, if desired.
+ *
+ * 5. It should be possible to allow multiple outstanding reads to be
+ * pending, to the limit of the page cache, but this potentially could
+ * require dealing with out of order completions of the reads. Essentially a
+ * list of completed buffer heads would be required to hold any completed
+ * buffer heads that cannot be sent prior to another, earlier request being
+ * completed.
+ *
+ * 6. Currently ioctl calls are available to start and stop device i/o.
+ *
+ * 7. The driver can optionally generate trace messages showing each sectors
+ * CRC as read or written with LBA. These can be compared by user programs to
+ * ensure that the correct data was read and/or written.
+ *
+ *
+ * TODO
+ *
+ * 1. error handling for block io, e.g. what if using with removable block
+ * device (like CF card) and it is removed.
+ *
+ * 2. Currently we memcpy() data from between the urb buffer and buffer
+ * head page. It should be possible to simply use the page buffer for the
+ * urb.
+ *
+ * 3. Should we offer using fileio as an option? This would allow direct access
+ * to a block device image stored in a normal file or direct access to (for example)
+ * ram disks. It would require implementing a separate file I/O kernel thread to
+ * do the actual I/O.
+ *
+ * 4. It may be interesting to support use of SCSI block device and pass the
+ * scsi commands directly to that. This would allow vendor commands for real
+ * devices to be passed through and executed with results being properly
+ * returned to the host. [This is the intended design for the mass storage
+ * specification.]
+ *
+ *
+ * TODO FIXME Bus Interface Notes
+ *
+ *
+ * @ingroup MSCFunction
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/otg-trace.h>
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#include <otg/otg-linux.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/atomic.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#if defined(LINUX26)
+#include <linux/bio.h>
+#include <linux/buffer_head.h>
+#endif
+#define CONFIG_OTG_MSC_BLOCK_TRACE 1
+#include <linux/blkdev.h>
+#include <linux/kdev_t.h>
+
+#include "msc-scsi.h"
+#include "msc.h"
+#include "msc-fd.h"
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+#include "crc.h"
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+#include "msc-io.h"
+
+
+//#include "rbc.h"
+
+
+/* Module Parameters ************************************************************************* */
+EMBED_LICENSE();
+
+MOD_PARM_INT (vendor_id, "Device Vendor ID", 0);
+MOD_PARM_INT (product_id, "Device Product ID", 0);
+MOD_PARM_INT (major, "Device Major", 0);
+MOD_PARM_INT (minor, "Device Minor", 0);
+
+
+#define DEVICE_EJECTED 0x0001 // MEDIA_EJECTED
+#define DEVICE_INSERTED 0x0002 // MEDIA_INSERT
+
+#define DEVICE_OPEN 0x0004 // WR_PROTECT_OFF
+#define DEVICE_WRITE_PROTECTED 0x0008 // WR_PROTECT_ON
+
+#define DEVICE_CHANGE_ON 0x0010 // MEDIA_CHANGE_ON
+
+#define DEVICE_PREVENT_REMOVAL 0x0020
+
+
+#define DEF_NUMBER_OF_HEADS 0x10
+#define DEF_SECTORS_PER_TRACK 0x20
+
+
+DECLARE_MUTEX(msc_sem);
+
+spinlock_t msc_lock;
+
+/* MSC ********************************************************************** */
+
+//XXX no use struct msc_private msc_private;
+
+int msc_urb_sent (struct usbd_urb *tx_urb, int rc);
+
+
+/* Block Device ************************************************************* */
+
+/*! msc_open_blockdev - open the block device specified in msc->major, msc->minor
+ *
+ * Sets appropriate fields to show current status of block device.
+ *
+ * XXX TODO - this needs to be tested against RO and absent devices.
+ *
+ * @param function_instance - pointer to this function instance
+ * @return none
+ *
+ */
+void msc_open_blockdev(struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+ int rc;
+ struct inode *inode;
+ request_queue_t *qqq;
+
+ spin_lock_init(&msc_lock);
+ down(&msc_sem);
+ msc->block_dev_state = DEVICE_EJECTED;
+
+ TRACE_MSG2(MSC, "OPEN BLOCKDEV: Major: %x Minor: %x", msc->major, msc->minor);
+
+ /*
+ * Check device information and verify access to the block device.
+ */
+ THROW_UNLESS(msc->major, ejected);
+
+ msc->dev = MKDEV(msc->major, msc->minor);
+
+#if defined(LINUX26)
+
+ THROW_UNLESS((msc->bdev = bdget(msc->dev)), ejected);
+
+
+
+ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0))) {
+
+ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW");
+ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0)), ejected);
+ msc->block_dev_state |= DEVICE_WRITE_PROTECTED;
+ }
+
+
+ printk(KERN_INFO"%s: bdev: %x\n", __FUNCTION__, msc->bdev);
+ printk(KERN_INFO"%s: bd_part: %x\n", __FUNCTION__, msc->bdev->bd_part);
+ printk(KERN_INFO"%s: bd_disk: %x\n", __FUNCTION__, msc->bdev->bd_disk);
+ printk(KERN_INFO"%s: capacity: %d\n", __FUNCTION__, msc->bdev->bd_disk->capacity);
+
+ #else /* defined(LINUX26) */ /*the same as linux2.4*/
+ THROW_IF (!(msc->bdev = bdget(kdev_t_to_nr(msc->dev))), ejected);
+ if ((rc = blkdev_get(msc->bdev, FMODE_READ | FMODE_WRITE, 0, BDEV_RAW))) {
+
+ TRACE_MSG0(MSC,"OPEN BLOCKDEV: cannot open RW");
+ THROW_IF ((rc = blkdev_get(msc->bdev, FMODE_READ, 0, BDEV_RAW)), ejected);
+ msc->block_dev_state |= DEVICE_WRITE_PROTECTED;
+ }
+
+#endif /* defined(LINUX26) */
+
+ msc->io_state = MSC_INACTIVE;
+ msc->block_dev_state &= ~DEVICE_EJECTED;
+ msc->block_dev_state |= DEVICE_INSERTED | DEVICE_CHANGE_ON;
+
+ TRACE_MSG1(MSC,"OPEN BLOCKDEV: opened block_dev_state: %x", msc->block_dev_state);
+
+ /*
+ * Note that capacity must return the LBA of the last addressable block
+ * c.f. RBC 4.4, RBC 5.3 and notes below RBC Table 6
+ *
+ * The blk_size array contains the number of addressable blocks or N,
+ * capacity is therefore N-1.
+ */
+
+#if defined(LINUX26)
+ TRACE_MSG0(MSC,"Linux 2.6");
+ msc->capacity = msc->bdev->bd_disk->capacity;
+ msc->block_size = bdev_hardsect_size(msc->bdev);
+ msc->max_blocks = PAGE_SIZE / msc->block_size;
+
+
+#else /* defined(LINUX26) */
+ TRACE_MSG0(MSC,"Linux 2.4");
+ msc->capacity = (blk_size[MAJOR(msc->dev)][MINOR(msc->dev)] << 1) - 1;
+ msc->block_size = get_hardsect_size(msc->dev);
+ msc->max_blocks = PAGE_SIZE / msc->block_size;
+
+ TRACE_MSG2(MSC,"blk_size: %x %d",
+ blk_size[MAJOR(msc->dev)][MINOR(msc->dev)] << 1,
+ blk_size[MAJOR(msc->dev)][MINOR(msc->dev)] << 1);
+#endif /* defined(LINUX26) */
+
+ TRACE_MSG2(MSC,"capacity: %x %d", msc->capacity, msc->capacity);
+ TRACE_MSG2(MSC,"block_size: %x %d", msc->block_size, msc->block_size);
+ TRACE_MSG2(MSC,"max_blocks: %x %d", msc->max_blocks, msc->max_blocks);
+
+ /* setup generic buffer_head
+ * XXX do we need two pages? it should be possible to have a single page
+ * for both read and write, in fact do we need a read_bh and write_bh?
+ * XXX ensure the page (or pages) get deallocated
+ */
+#if defined(LINUX26)
+ printk(KERN_INFO "BIO,start\n");
+ msc->write_pending = msc->read_pending = 0;
+ bio_init(&msc->write_bio);
+ bio_init(&msc->read_bio);
+ msc->write_bio.bi_private = msc->read_bio.bi_private = function_instance;
+
+ msc->read_bio.bi_io_vec=&msc->rbio_vec;
+ msc->rbio_vec.bv_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
+ msc->rbio_vec.bv_len = 0;
+ msc->rbio_vec.bv_offset = 0;
+ msc->read_bio.bi_vcnt = 1;
+ msc->read_bio.bi_idx = 0;
+ msc->read_bio.bi_size = 0;
+ msc->read_bio.bi_bdev = msc->bdev;
+ msc->read_bio.bi_sector = 0;
+
+ msc->write_bio.bi_io_vec=&msc->wbio_vec;
+ msc->wbio_vec.bv_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
+ msc->wbio_vec.bv_len = 0;
+ msc->wbio_vec.bv_offset = 0;
+ msc->write_bio.bi_vcnt = 1;
+ msc->write_bio.bi_idx = 0;
+ msc->write_bio.bi_size = 0;
+ msc->write_bio.bi_bdev = msc->bdev;
+ msc->write_bio.bi_sector = 0;
+ printk(KERN_INFO "BIO,end\n");
+
+#else
+ msc->write_pending = msc->read_pending = 0;
+ msc->write_bh.b_private = msc->read_bh.b_private = function_instance;
+ msc->read_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
+ msc->write_bh.b_page = alloc_page(GFP_NOIO); // XXX ensure that this gets de-allocated
+ msc->read_bh.b_data = page_address(msc->read_bh.b_page);
+ msc->write_bh.b_data = page_address(msc->write_bh.b_page);
+
+ msc->write_bh.b_rdev = msc->read_bh.b_rdev = msc->dev;
+#endif /* defined(LINUX26) */
+
+ qqq=bdev_get_queue(msc->bdev);
+ if(!(qqq->queue_lock)) qqq->queue_lock=&msc_lock;
+
+ CATCH(ejected) {
+ TRACE_MSG1(MSC,"OPEN BLOCKDEV: EJECTED block_dev_state: %x", msc->block_dev_state);
+ printk(KERN_INFO"%s: Cannot get device %d %d\n", __FUNCTION__, msc->major, msc->minor);
+ }
+ up(&msc_sem);
+}
+
+/*! msc_close_blockdev - close the device for host
+ * @param function_instance - pointer to this function instance
+ * @return none
+ */
+
+void msc_close_blockdev(struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+
+ RETURN_IF(msc->block_dev_state == DEVICE_EJECTED);
+
+ // XXX this should be a wait for read_pending/write_pending to go to ZERO
+
+#if defined(LINUX26)
+
+ {
+ static DECLARE_WAIT_QUEUE_HEAD(msc1_wq);
+ printk(KERN_INFO"%s: read_pending:%x,write_pending:%x\n", __FUNCTION__,msc->read_pending,msc->write_pending);
+ wait_event_interruptible(msc1_wq, ((msc->read_pending+msc->write_pending)==0));
+ }
+
+#else /* defined(LINUX26) */
+
+ while (msc->read_pending || msc->write_pending) {
+ printk(KERN_INFO"%s: sleeping on read or write bh\n", __FUNCTION__);
+ sleep_on_timeout(&msc->msc_wq, 20);
+ }
+
+#endif /* defined(LINUX26) */
+
+ down(&msc_sem);
+ msc->block_dev_state = DEVICE_EJECTED;
+
+#if defined(LINUX26)
+ if (msc->bdev)
+ blkdev_put(msc->bdev);
+#else /* defined(LINUX26) */
+ if (msc->bdev)
+ blkdev_put(msc->bdev, BDEV_RAW);
+#endif /* defined(LINUX26) */
+
+#if defined(LINUX26)
+ __free_page(msc->rbio_vec.bv_page);
+ msc->rbio_vec.bv_page = NULL;
+ __free_page(msc->wbio_vec.bv_page);
+ msc->wbio_vec.bv_page = NULL;
+
+#else /* defined(LINUX26) */
+ __free_page((void *)&msc->read_bh.b_page);
+ msc->read_bh.b_page = NULL;
+ __free_page((void *)&msc->write_bh.b_page);
+ msc->write_bh.b_page = NULL;
+#endif /* defined(LINUX26) */
+ up(&msc_sem);
+}
+
+
+/* READ 10 COMMAND - read and send data to the host ******************************************** */
+
+int msc_start_reading_block_data(struct usbd_function_instance *function);
+
+#if defined(LINUX26)
+
+static int msc_block_read_finished(struct bio *bio,unsigned int bytes_done,int err);
+
+#else /* defined(LINUX26) */
+
+void msc_block_read_finished(struct buffer_head *bh, int flag);
+
+#endif /* defined(LINUX26) */
+
+/*! msc_scsi_read_10 - process a read(10) command
+ *
+ * We have received a READ(10) CBW, if transfer length is non-zero
+ * initiate a generic block i/o otherwise send a CSW.
+ *
+ * Returns non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_read_10(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_read_10_command *command = (struct msc_scsi_read_10_command *)&msc->command.CBWCB;
+
+ /*
+ * save the CBW information and setup for transmitting data read from the block device
+ */
+
+ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
+ TRACE_MSG1(MSC,"sos msc->lba:%d",msc->lba);
+ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
+ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
+ msc->command_state = MSC_DATA_IN_READ;
+ msc->io_state = MSC_INACTIVE;
+
+ /*Case 1: If Host Expect no data, while device has data, then report phase error directly */
+
+ if((msc->command.dCBWDataTransferLength==0) && (msc->TransferLength_in_blocks>0)){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ return 0;
+ }
+
+ /*Case 2:simply send the CSW if the host didn't actually ask for a non-zero length.*/
+
+ if((msc->command.dCBWDataTransferLength==0) && (msc->TransferLength_in_blocks==0)){
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ }
+
+ /*Case 3: If Host Expect data, while device has no data, then device stalls BulkIn and put to CBW_PASSED state */
+ if((msc->command.dCBWDataTransferLength>0) && (msc->TransferLength_in_blocks==0)){
+
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PASSED;
+ return 0;
+ }
+
+ /* Case 10 Host expects data OUT, but Device reads data: Stall BulkOut and CBW phase error */
+ if ((msc->TransferLength_in_blocks) && (msc->command.bmCBWFlags & 0x80)==0){
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ return 0;
+ }
+ /* Host expects data and Device has to read data: Start reading blocks to send */
+
+ if ((msc->TransferLength_in_blocks) && (msc->command.bmCBWFlags & 0x80)){
+ return msc_start_reading_block_data(function_instance);
+
+ }
+ return 0;
+/*
+ return (msc->TransferLength_in_blocks) ? msc_start_reading_block_data(function_instance) :
+ msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+
+*/
+}
+
+
+/*! msc_start_reading_block_data - start reading data
+ *
+ * Generate a generic block io request to read some data to transmit.
+ *
+ * This function is initially called by msc_scsi_read_10() but can also be called
+ * by msc_urb_sent() if the amount of data requested was too large.
+ *
+ * The function msc_block_read_finished() will be called to actually send the data
+ * to the host when the I/O request is complete.
+ *
+ */
+int msc_start_reading_block_data(struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
+ unsigned long flags;
+ char *btemp_data;
+ request_queue_t *qqq;
+ /*TRACE_MSG2(MSC,"START READING BLOCK DATA lba: %x blocks: %d %d ",
+ msc->lba, msc->TransferLength_in_blocks);
+ */
+ if(msc->TransferLength_in_blocks<=0)
+ printk(KERN_INFO "msc->TransferLength_in_blocks111=0\n");
+ /*else
+ printk(KERN_INFO "START READING BLOCK DATA lba111: %x blocks: %d \n",msc->lba, msc->TransferLength_in_blocks);
+ */
+ /* ensure that device state is ok
+ */
+ if (msc->block_dev_state != DEVICE_INSERTED) {
+ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT");
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_MEDIA_NOT_PRESENT,
+ msc->lba, USB_MSC_FAILED);
+ }
+
+ // XXX an ioctl has requested that pending io be aborted
+ local_irq_save(flags);
+ if (msc->io_state & MSC_ABORT_IO) {
+ msc->io_state &= ~MSC_ABORT_IO;
+ TRACE_MSG0(MSC,"BLOCK READ ABORTED");
+ local_irq_restore(flags);
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR,
+ msc->lba, USB_MSC_FAILED);
+ }
+ local_irq_restore(flags);
+
+ /* sanity check lba against capacity
+ */
+ if (msc->lba >= msc->capacity) {
+ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
+ return msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
+ }
+
+ /* setup buffer head - msc_block_read_finished() will be called when block i/o is finished
+ */
+
+#if defined(LINUX26)
+ if(TransferLength_in_blocks<=0)
+ printk(KERN_INFO "TransferLength_in_blocks==0");
+
+ /*else
+ printk(KERN_INFO "Start reading blocks: %x\n", TransferLength_in_blocks);*/
+
+
+ msc->rbio_vec.bv_len = TransferLength_in_blocks * msc->block_size;;
+ msc->rbio_vec.bv_offset = 0;
+ msc->read_bio.bi_vcnt = 1;
+ msc->read_bio.bi_idx = 0;
+ msc->read_bio.bi_size = TransferLength_in_blocks * msc->block_size;
+ msc->read_bio.bi_bdev = msc->bdev;
+ msc->read_bio.bi_sector = msc->lba;
+ msc->read_bio.bi_end_io = msc_block_read_finished;
+ msc->io_state |= MSC_BLOCKIO_PENDING;
+ msc->read_pending=1;
+
+
+ //btemp_data = page_address(msc->read_bio.bi_io_vec->bv_page);
+
+ btemp_data=__bio_kmap_atomic( &msc->read_bio,0,KM_USER0);
+
+ memset(btemp_data, 0x0, msc->read_bio.bi_size);
+ __bio_kunmap_atomic(btemp_data,KM_USER0);
+
+ //generic_make_request(READ, &msc->read_bio);
+ if (msc->read_bio.bi_size){
+
+ /* printk(KERN_INFO "Start reading bio->size: %x\n", msc->read_bio.bi_size); */
+ submit_bio(READ, &msc->read_bio);
+ generic_unplug_device(bdev_get_queue(msc->bdev));
+ }
+ else
+ {
+ printk(KERN_INFO "msc->read_bio.bi_size=0\n");
+ }
+
+
+#else /* defined(LINUX26) */
+
+ msc->read_bh.b_end_io = msc_block_read_finished;
+ msc->read_bh.b_size = TransferLength_in_blocks * msc->block_size;
+ msc->read_bh.b_rsector = msc->lba;
+ msc->read_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
+ msc->io_state |= MSC_BLOCKIO_PENDING;
+ msc->read_pending=1;
+ memset(msc->read_bh.b_data, 0x0, msc->read_bh.b_size);
+ generic_make_request(READ, &msc->read_bh);
+ generic_unplug_device(blk_get_queue(msc->read_bh.b_rdev));
+
+#endif /* defined(LINUX26) */
+ return 0;
+}
+
+
+/*! msc_block_read_finished - called by generic request
+ *
+ * Called when block i/o read is complete, send the data to the host if possible.
+ *
+ * The function msc_urb_sent() will be called when the data is sent to
+ * either send additional data or the CSW as appropriate.
+ *
+ * If more data is required then call msc_start_reading_block_data() to
+ * issue another block i/o to get more data.
+ *
+ * These means that there can be two outstanding actions when this
+ * function completes:
+ *
+ * 1. a transmit urb may be pending, sending the most recently
+ * read data to the host
+ * 2. another block i/o may be pending to read additional data
+ *
+ * This leads to a race condition, if the block i/o finished before the urb
+ * transmit, then we must simply exit. The msc_in_read_10_urb_sent()
+ * function will ensure that this is restarted.
+ */
+
+#if defined(LINUX26)
+
+static int msc_block_read_finished(struct bio *bio,unsigned int bytes_done,int err)
+{
+ struct usbd_function_instance *function_instance = bio->bi_private;
+ struct msc_private *msc = function_instance->privdata;
+ //int TransferLength_in_blocks = bh->b_size / msc->block_size;
+
+ int TransferLength_in_blocks;
+
+ struct usbd_urb *tx_urb;
+ unsigned long flags;
+ int rc;
+ int i;
+ int bytes_done1;
+ char *btemp_data;
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ u32 crc;
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+
+ if(bio->bi_size)
+ return 1;
+
+ //msc = bh->b_private;
+ TransferLength_in_blocks = bytes_done / msc->block_size;
+
+ TRACE_MSG1(MSC," bio BLOCK READ FINISHED size: %x", bytes_done);
+
+ wake_up_interruptible(&msc->ioctl_wq);
+
+ /*
+ * Race condition here, if we have not finished sending the
+ * previous tx_urb then we want to simply remmber the parameters ,exit and let
+ * msc_in_read_10_urb_sent() call us again.
+ *
+ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and
+ * that we do reset BLOCKIO if not SEND PENDING
+ */
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (msc->io_state & MSC_SEND_PENDING) {
+ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING");
+ msc->io_state |= MSC_BLOCKIO_FINISHED;
+ msc->bytes_done = bytes_done;
+ msc->err = err;
+ local_irq_restore(flags);
+ return 0 ;
+ }
+ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED);
+ local_irq_restore(flags);
+ }
+
+ /* verify that the I/O completed
+ */
+ if (err) {
+ TRACE_MSG0(MSC,"BLOCK READ FAILED");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
+ return 0;
+ }
+
+ bytes_done1=bytes_done;
+
+ /* allocate a tx_urb and copy into it
+ */
+ //THROW_IF(!(function = msc->function), error);
+
+ /* To see if enough data has been read. We only send as many as Host expects and don't read more*/
+
+ if ((msc->data_transferred_in_bytes+bytes_done)>msc->command.dCBWDataTransferLength){
+ bytes_done1=msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes;
+ }
+
+ THROW_IF(!(tx_urb = usbd_alloc_urb (function_instance, BULK_IN, bytes_done1, msc_urb_sent)), error);
+ TRACE_MSG2(MSC,"bio BLOCK READ FINISHED urb: %p bytes_done:%d ", (int)tx_urb, bytes_done1);
+
+ tx_urb->function_privdata = function_instance;
+ tx_urb->actual_length = tx_urb->buffer_length = bytes_done1;
+
+
+ //btemp_data = page_address(msc->read_bio.bi_io_vec->bv_page);
+ btemp_data=__bio_kmap_atomic(&msc->read_bio,0,KM_USER0);
+
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ TRACE_MSG0(MSC,"sos CONFIG_OTG_MSC_BLOCK_TRACE");
+ /* debug output trace - dump rlba and computed crc
+ */
+ for (i = 0; i < bytes_done / msc->block_size; i++) {
+ crc = crc32_compute(btemp_data + (i * msc->block_size), msc->block_size, CRC32_INIT);
+ TRACE_RLBA(bio->bi_sector + i, crc);
+ }
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+
+ memcpy(tx_urb->buffer, btemp_data, bytes_done1);
+
+ __bio_kunmap_atomic(btemp_data,KM_USER0);
+
+
+
+ msc->read_pending=0;
+
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+
+ msc->io_state |= MSC_SEND_PENDING;
+ msc->TransferLength_in_bytes -= bytes_done1;
+ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
+ msc->lba += TransferLength_in_blocks;
+
+ if ((!msc->TransferLength_in_blocks) || (bytes_done>bytes_done1)) {
+ TRACE_MSG0(MSC,"bio BLOCK READ FINISHED - IO FINISHED");
+ // set flag so that CSW can be sent
+ msc->io_state |= MSC_DATA_IN_READ_FINISHED;
+ }
+ local_irq_restore(flags);
+ }
+
+ /* dispatch urb - msc_urb_sent() will be called when urb is finished
+ */
+ if ((rc = usbd_start_in_urb (tx_urb))) {
+ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED");
+ usbd_free_urb (tx_urb);
+ THROW(error);
+ }
+
+ /* if more data is required then call msc_start_reading_block_data() to
+ * issue another block i/o to get more data.
+ */
+ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) {
+ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING");
+ msc_start_reading_block_data(function_instance);
+ }
+
+ CATCH(error) {
+ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR");
+ // stall?
+ }
+ return 0;
+}
+
+
+
+
+#else /* defined(LINUX26) */
+void msc_block_read_finished(struct buffer_head *bh, int uptodate)
+{
+ struct usbd_function_instance *function_instance = bh->b_private;
+ struct msc_private *msc = function_instance->privdata;
+ //int TransferLength_in_blocks = bh->b_size / msc->block_size;
+
+ int TransferLength_in_blocks;
+
+ struct usbd_urb *tx_urb;
+ unsigned long flags;
+ int rc;
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ u32 crc;
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+ int i;
+
+ //msc = bh->b_private;
+ TransferLength_in_blocks = bh->b_size / msc->block_size;
+
+ TRACE_MSG1(MSC,"BLOCK READ FINISHED size: %x", bh->b_size);
+
+ wake_up_interruptible(&msc->ioctl_wq);
+
+ /*
+ * Race condition here, if we have not finished sending the
+ * previous tx_urb then we want to simply exit and let
+ * msc_in_read_10_urb_sent() call us again.
+ *
+ * Ensure that we do not reset BLOCKIO flags if SEND PENDING and
+ * that we do reset BLOCKIO if not SEND PENDING
+ */
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (msc->io_state & MSC_SEND_PENDING) {
+ TRACE_MSG0(MSC,"BLOCK READ SEND PENDING");
+ msc->io_state |= MSC_BLOCKIO_FINISHED;
+ msc->uptodate = uptodate;
+ local_irq_restore(flags);
+ return;
+ }
+ msc->io_state &= ~(MSC_BLOCKIO_PENDING | MSC_BLOCKIO_FINISHED);
+ local_irq_restore(flags);
+ }
+
+ /* verify that the I/O completed
+ */
+ if (1 != uptodate) {
+ TRACE_MSG0(MSC,"BLOCK READ FAILED");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
+ return;
+ }
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ /* debug output trace - dump rlba and computed crc
+ */
+ for (i = 0; i < bh->b_size / msc->block_size; i++) {
+ crc = crc32_compute(bh->b_data + (i * msc->block_size), msc->block_size, CRC32_INIT);
+ TRACE_RLBA(bh->b_rsector + i, crc);
+ }
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+ /* allocate a tx_urb and copy into it
+ */
+ //THROW_IF(!(function = msc->function), error);
+ THROW_IF(!(tx_urb = usbd_alloc_urb (function_instance, BULK_IN, bh->b_size, msc_urb_sent)), error);
+ TRACE_MSG1(MSC,"BLOCK READ FINISHED urb: %p", (int)tx_urb);
+
+ tx_urb->function_privdata = function_instance;
+ tx_urb->actual_length = tx_urb->buffer_length = bh->b_size;
+ memcpy(tx_urb->buffer, bh->b_data, bh->b_size);
+
+ msc->read_pending=0;
+
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+
+ msc->io_state |= MSC_SEND_PENDING;
+ msc->TransferLength_in_bytes -= bh->b_size;
+ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
+ msc->lba += TransferLength_in_blocks;
+
+ if (!msc->TransferLength_in_blocks) {
+ TRACE_MSG0(MSC,"BLOCK READ FINISHED - IO FINISHED");
+ // set flag so that CSW can be sent
+ msc->io_state |= MSC_DATA_IN_READ_FINISHED;
+ }
+ local_irq_restore(flags);
+ }
+
+ /* dispatch urb - msc_urb_sent() will be called when urb is finished
+ */
+ if ((rc = usbd_start_in_urb (tx_urb))) {
+ TRACE_MSG0(MSC,"BLOCK READ FINISHED FAILED");
+ usbd_free_urb (tx_urb);
+ THROW(error);
+ }
+
+ /* if more data is required then call msc_start_reading_block_data() to
+ * issue another block i/o to get more data.
+ */
+ if (!(msc->io_state & MSC_DATA_IN_READ_FINISHED)) {
+ TRACE_MSG0(MSC,"BLOCK READ SEND RESTARTING");
+ msc_start_reading_block_data(function_instance);
+ }
+
+ CATCH(error) {
+ TRACE_MSG0(MSC,"BLOCK READ FINISHED ERROR");
+ // stall?
+ }
+}
+
+#endif /* defined(LINUX26) */
+
+
+/*! msc_in_read_10_urb_sent - called by msc_urb_sent when state is MSC_DATA_IN_READ
+ *
+ * This will process a read_10 urb that has been sent. Specifically if any previous
+ * read_10 block I/O has finished we recall the msc_block_read_finished() function
+ * to transmit another read_10 urb.
+ *
+ * If there is no other pending read_10 to do we create and send a CSW.
+ *
+ * If there is more I/O to do or there is already an outstanding I/O we simply
+ * return after freeing the URB.
+ *
+ * Return non-zero if urb was not disposed of.
+ */
+int msc_in_read_10_urb_sent(struct usbd_urb *tx_urb)
+{
+ struct usbd_function_instance *function_instance = tx_urb->function_privdata;
+ struct msc_private *msc = function_instance->privdata;
+ unsigned long flags;
+
+ TRACE_MSG0(MSC,"URB SENT DATA IN");
+
+ /*
+ * Potential race condition here, we may need to restart blockio.
+ */
+ local_irq_save(flags);
+
+ msc->io_state &= ~MSC_SEND_PENDING;
+ msc->data_transferred_in_bytes += tx_urb->actual_length;
+
+ TRACE_MSG1(MSC,"URB SENT DATA IN data transferred: %d", msc->data_transferred_in_bytes);
+
+ if (!tx_urb->actual_length)
+ msc->TransferLength_in_blocks = 0;
+
+ /* XXX We should be checking urb status
+ */
+
+ usbd_free_urb (tx_urb);
+
+ /*
+ * Check to see if we need to send CSW.
+ */
+ if (MSC_DATA_IN_READ_FINISHED & msc->io_state) {
+ /* Case 5: Host expect more data Hi>Di Then stall BulkIn ,later send CSW*/
+ if (msc->data_transferred_in_bytes < msc->command.dCBWDataTransferLength){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PASSED;
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ /* Case 6: Hi = Di */
+ if (msc->TransferLength_in_bytes==0){
+ TRACE_MSG0(MSC,"URB SENT DATA IN - FINISHED SENDING CSW");
+ local_irq_restore(flags);
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ }
+
+ /*Case 7 Host expects less data Hi < Di, Then Stall after transfer exected length */
+
+ if (msc->TransferLength_in_bytes>0){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ local_irq_restore(flags);
+ return 0;
+ }
+ }
+ /*
+ * Check to see if there is a block read needs to be finished.
+ */
+ if (MSC_BLOCKIO_FINISHED & msc->io_state) {
+ TRACE_MSG0(MSC,"URB SENT DATA IN - RESTART");
+ local_irq_restore(flags);
+ // XXX uptodate?
+#if defined(LINUX26)
+ msc_block_read_finished(&msc->read_bio, msc->bytes_done, msc->err);
+#else /* defined(LINUX26) */
+ msc_block_read_finished(&msc->read_bh, msc->uptodate);
+#endif /* defined(LINUX26) */
+ return 0;
+ }
+ local_irq_restore(flags);
+ return 0;
+}
+
+
+/* WRITE 10 - receive data from host and write to block device ********************************* */
+
+int msc_start_receiving_data(struct usbd_function_instance *function);
+
+#if defined(LINUX26)
+static int msc_data_written(struct bio *bio, unsigned bytes_done,int err);
+#else /* defined(LINUX26) */
+void msc_data_written(struct buffer_head *bh, int flag);
+#endif /* defined(LINUX26) */
+
+void msc_data_test(struct buffer_head *bh, int flag);
+
+/*! int msc_scsi_write_10(struct usbd_function_instance*, char* ,int )
+ * @brief - process a write command
+ *
+ * Call either msc_start_receiving_data() or msc_start_sending_csw() as appropriate.
+ * Normally msc_start_receiving_data() is called and it will use msc_start_recv_urb()
+ * to setup a receive urb of the appropriate size.
+ *
+ * @param function_instance - pointer to this function instance
+ * @param name
+ * @param op
+ * @return non-zero if there is an error in the USB layer.
+ */
+int msc_scsi_write_10(struct usbd_function_instance *function_instance, char *name, int op)
+{
+ struct msc_private *msc = function_instance->privdata;
+ struct msc_scsi_write_10_command *command = (struct msc_scsi_write_10_command *)&msc->command.CBWCB;
+
+ /* save the CBW and setup for receiving data to be written to the block device
+ */
+ msc->lba = be32_to_cpu(command->LogicalBlockAddress);
+ msc->TransferLength_in_blocks = be16_to_cpu(command->TransferLength);
+ msc->TransferLength_in_bytes = msc->TransferLength_in_blocks * msc->block_size;
+
+ msc->command_state = MSC_DATA_OUT_WRITE;
+ msc->io_state = MSC_INACTIVE;
+
+ TRACE_MSG1(MSC,"RECV WRITE lba: %x", msc->lba);
+ TRACE_MSG1(MSC,"RECV WRITE blocks: %d", msc->TransferLength_in_blocks);
+
+
+ /*Case 3: If Host Expect no data, device writes data Hn < Do then report phase error directly */
+
+ if((msc->command.dCBWDataTransferLength==0) && (msc->TransferLength_in_blocks>0)){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ return 0;
+ }
+
+ /*Case9: Host sends data, but Device doesn't expect to rcv data*/
+
+ if((msc->command.dCBWDataTransferLength>0) && (msc->TransferLength_in_blocks==0)){
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = USB_MSC_PASSED;
+ return 0;
+ }
+
+ /* Case 8 Host expect data IN, but Device writes data: Stall BulkIn and CBW phase error */
+ if ((msc->TransferLength_in_blocks) && (msc->command.bmCBWFlags & 0x80)){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ return 0;
+ }
+
+ /*Case 1: Hn = Dn */
+
+ if((msc->command.dCBWDataTransferLength==0) && (msc->TransferLength_in_blocks==0)){
+ return msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ }
+
+ return msc_start_receiving_data(function_instance);
+
+}
+
+
+/*! msc_start_receiving_data(struct usbd_function_instance * )
+ * @brief - called to initiate an urb to receive WRITE(10) data
+ *
+ * Initiate a receive urb to receive upto PAGE_SIZE WRITE(10) data. The
+ * msc_recv_urb() function will call msc_recv_out_blocks() to actually
+ * write the data.
+ *
+ * This is called from msc_scsi_write_10() to initiate the WRITE(10) and
+ * called from msc_data_written() to start the next page sized receive
+ * urb.
+ *
+ * @param function_instance
+ * @return non-zero for error
+ */
+int msc_start_receiving_data(struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+ u32 minmumbytes=0;
+ /*
+ * Calculating the length is most of the work we do :-)
+ */
+ int TransferLength_in_blocks = MIN(msc->max_blocks, msc->TransferLength_in_blocks);
+
+ TRACE_MSG1(MSC,"START RECEIVING DATA lba: %x", msc->lba);
+ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", msc->TransferLength_in_blocks);
+ TRACE_MSG1(MSC,"START RECEIVING DATA blocks: %d", TransferLength_in_blocks);
+ TRACE_MSG1(MSC,"START RECEIVING DATA bytes: %d", TransferLength_in_blocks * msc->block_size);
+
+/* Ensure Host will send the length data*/
+ minmumbytes=
+MIN((msc->command.dCBWDataTransferLength - msc->data_transferred_in_bytes),TransferLength_in_blocks * msc->block_size);
+
+ THROW_IF(msc->command_state != MSC_DATA_OUT_WRITE, error);
+ THROW_IF(!msc->TransferLength_in_blocks, error);
+
+ msc->io_state |= MSC_RECV_PENDING;
+
+ //return msc_start_recv_urb(function_instance, TransferLength_in_blocks * msc->block_size);
+ return msc_start_recv_urb(function_instance, minmumbytes);
+ CATCH(error) {
+ TRACE_MSG0(MSC,"START RECEIVING DATA ERROR");
+ return -EINVAL;
+ }
+}
+
+
+/*! void msc_recv_out_blocks(struct usbd_urb )
+ * @brief - process received WRITE(10) data by writing to block device
+ *
+ * We get here indirectly from msc_recv_urb() when state is MSC_DATA_OUT_WRITE.
+ *
+ * Dealloc the urb and call Initiate the generic request to write the
+ * received data. The b_end_io function msc_data_written() will send the
+ * CSW.
+ *
+ * @param rcv_urb - pointer to recv urb
+ * @return none
+ *
+ */
+void msc_recv_out_blocks(struct usbd_urb *rcv_urb)
+{
+ struct usbd_function_instance *function_instance = rcv_urb->function_privdata;
+ struct msc_private *msc = function_instance->privdata;
+ int TransferLength_in_bytes = rcv_urb->actual_length;
+ int TransferLength_in_blocks = TransferLength_in_bytes / msc->block_size;
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ u32 crc;
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+ int i;
+#if defined(LINUX26)
+ char *btemp_data;
+#endif
+ TRACE_MSG1(MSC,"RECV OUT bytes: %d", TransferLength_in_bytes);
+ TRACE_MSG1(MSC,"RECV OUT iostate: %x", msc->io_state);
+ TRACE_MSG1(MSC,"RECV OUT data transferred: %d", msc->data_transferred_in_bytes);
+ //if(TransferLength_in_bytes)
+ //printk(KERN_INFO "sos0 TransferLength_in_bytes: %x\n", TransferLength_in_bytes);
+ // else
+ //printk(KERN_INFO "sos0 write TransferLength_in_bytes = 0\n");
+
+
+ /*
+ * Race condition here, we may get to here before the previous block
+ * write completed. If so just exit and the msc_data_written()
+ * function will recall us.
+ */
+ {
+ unsigned long flags;
+ local_irq_save(flags);
+ if (msc->io_state & MSC_BLOCKIO_PENDING) {
+ TRACE_MSG0(MSC,"RECV OUT BLOCKIO PENDING");
+ msc->io_state |= MSC_RECV_FINISHED;
+ msc->rcv_urb_finished = rcv_urb;
+ local_irq_restore(flags);
+ return;
+ }
+ msc->io_state &= ~(MSC_RECV_PENDING |MSC_RECV_FINISHED);
+ local_irq_restore(flags);
+ }
+
+ msc->data_transferred_in_bytes += rcv_urb->actual_length;
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ for (i = 0; i < TransferLength_in_blocks; i++) {
+ crc = crc32_compute(rcv_urb->buffer + (i * msc->block_size), msc->block_size, CRC32_INIT);
+ TRACE_SLBA(msc->lba + i, crc);
+ }
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+
+ msc->write_pending=1;
+#if defined(LINUX26)
+
+ btemp_data=__bio_kmap_atomic( &msc->write_bio,0,KM_USER0);
+
+ //btemp_data = page_address(msc->write_bio.bi_io_vec->bv_page);
+ memcpy(btemp_data,rcv_urb->buffer, TransferLength_in_bytes);
+
+ __bio_kunmap_atomic(btemp_data,KM_USER0);
+#else
+ memcpy(msc->write_bh.b_data, rcv_urb->buffer, rcv_urb->actual_length);
+#endif
+ usbd_free_urb(rcv_urb);
+
+ /* ensure media state is ok
+ */
+ if (msc->block_dev_state != DEVICE_INSERTED) {
+ TRACE_MSG0(MSC,"START READ MEDIA NOT PRESENT");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_MEDIA_NOT_PRESENT, msc->lba, USB_MSC_FAILED);
+ return;
+ }
+
+ // XXX an ioctl has requested that pending io be aborted
+ if (msc->io_state & MSC_ABORT_IO) {
+ unsigned long flags;
+ local_irq_save(flags);
+ msc->io_state &= ~MSC_ABORT_IO;
+ TRACE_MSG0(MSC,"BLOCK READ ABORTED");
+ local_irq_restore(flags);
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
+ return;
+ }
+
+ /* sanity check lba against capacity
+ */
+ if (msc->lba >= msc->capacity) {
+ TRACE_MSG2(MSC, "START READ LBA out of range: lba: %d capacity: %d", msc->lba, msc->capacity);
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
+ return;
+ }
+
+ /* additional sanity check for lba - ensure non-zero
+ */
+ if (!msc->TransferLength_in_blocks) {
+ TRACE_MSG0(MSC,"START READ LBA TransferLength_in_blocks zero:");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE, msc->lba, USB_MSC_FAILED);
+ return;
+ }
+
+ /* XXX additional sanity check required here - verify that the transfer
+ * size agrees with what we where expecting, specifically I think
+ * we need to verify that we have received a multiple of the block
+ * size and either a full bulk transfer (so more will come) or
+ * the actual exact amount that the host said it would send.
+ * An error should generate a CSW with a USB_MSC_PHASE_ERROR
+ */
+
+
+ TRACE_MSG1(MSC,"RECV OUT lba: %x", msc->lba);
+ TRACE_MSG1(MSC,"RECV OUT blocks left: %d", msc->TransferLength_in_blocks);
+ TRACE_MSG1(MSC,"RECV OUT blocks current: %d", TransferLength_in_blocks);
+
+ /* Initiate writing the data - msc_data_written() will be called
+ * when finished.
+ */
+ //if(TransferLength_in_bytes)
+ //printk(KERN_INFO "sos TransferLength_in_bytes: %x\n", TransferLength_in_bytes);
+ // else
+ //printk(KERN_INFO"sos write TransferLength_in_bytes = 0\n");
+
+#if defined(LINUX26)
+
+ msc->wbio_vec.bv_offset = 0;
+ msc->write_bio.bi_vcnt = 1;
+ msc->write_bio.bi_idx = 0;
+ msc->write_bio.bi_size = TransferLength_in_bytes;
+ msc->wbio_vec.bv_len = TransferLength_in_bytes;
+ msc->write_bio.bi_bdev = msc->bdev;
+ msc->write_bio.bi_sector = msc->lba;
+ msc->write_bio.bi_end_io = msc_data_written;
+ msc->io_state |= MSC_BLOCKIO_PENDING;
+ msc->write_pending=1;
+#if 0
+ /* decrement counters and increment address
+ */
+ msc->TransferLength_in_bytes -= TransferLength_in_bytes;
+ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
+ msc->lba += TransferLength_in_blocks;
+
+ /* Check current status of TransferLength - if non-zero then we need
+ * to queue another urb to receive more data.
+ */
+ //if (!msc->TransferLength_in_blocks)
+ if ( msc->data_transferred_in_bytes > msc->command.dCBWDataTransferLength
+ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
+ else
+ msc_start_receiving_data(function_instance);
+#else
+ /* Check current status of TransferLength - if non-zero then we need
+ * to queue another urb to receive more data.
+ */
+ //if (!msc->TransferLength_in_blocks)
+
+ /*Case 13 Ho < Do */
+ if((msc->data_transferred_in_bytes >= msc->command.dCBWDataTransferLength) &&
+ (msc->TransferLength_in_bytes>TransferLength_in_bytes)) {
+ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
+ TRACE_MSG0( MSC, "Case 13");
+ msc->caseflag=13;
+ }
+
+ /*Case 11 Ho > Do */
+ else if ( msc->TransferLength_in_blocks <=TransferLength_in_blocks){
+
+ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
+ if(msc->command.dCBWDataTransferLength>msc->data_transferred_in_bytes) {
+ msc->write_bio.bi_size = msc->TransferLength_in_bytes;
+ msc->wbio_vec.bv_len = msc->TransferLength_in_bytes;
+ msc->caseflag=11;
+ TRACE_MSG0( MSC, "Case 11");
+ }else{
+ /* Case 12 Ho = Do */
+ msc->caseflag=12;
+ TRACE_MSG0( MSC, "Case 12");
+ }
+ }
+
+
+ else {
+
+
+ /* decrement counters and increment address
+ */
+ msc->TransferLength_in_bytes -= TransferLength_in_bytes;
+ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
+ msc->lba += TransferLength_in_blocks;
+ msc_start_receiving_data(function_instance);
+ }
+
+
+#endif
+
+ /* initiate the block i/o request
+ */
+ if(msc->write_bio.bi_size){
+ /*printk(KERN_INFO"write OUT bio->size: %x\n", msc->write_bio.bi_size);*/
+ submit_bio(WRITE, &msc->write_bio);
+ generic_unplug_device(bdev_get_queue(msc->bdev));
+ }
+ else
+ {
+ printk(KERN_INFO"write OUT bio->size=0\n");
+ }
+
+
+
+#else /* defined(LINUX26) */
+
+ msc->write_bh.b_end_io = msc_data_written;
+
+ /* set address in buffer head
+ */
+ msc->write_bh.b_size = TransferLength_in_bytes;
+ msc->write_bh.b_rsector = msc->lba;
+
+ /* decrement counters and increment address
+ */
+ msc->TransferLength_in_bytes -= TransferLength_in_bytes;
+ msc->TransferLength_in_blocks -= TransferLength_in_blocks;
+ msc->lba += TransferLength_in_blocks;
+
+ msc->write_bh.b_state = (1UL << BH_Mapped) | (1UL << BH_Lock);
+
+ msc->io_state |= MSC_BLOCKIO_PENDING;
+
+
+ /* Check current status of TransferLength - if non-zero then we need
+ * to queue another urb to receive more data.
+ */
+ if (!msc->TransferLength_in_blocks)
+ msc->command_state = MSC_DATA_OUT_WRITE_FINISHED;
+ else
+ msc_start_receiving_data(function_instance);
+
+ /* initiate the block i/o request
+ */
+
+ generic_make_request(WRITE, &msc->write_bh);
+ generic_unplug_device(blk_get_queue(msc->write_bh.b_rdev));
+#endif /* defined(LINUX26) */
+}
+
+
+/*! int msc_data_written(struct bio *, unsigned , int)
+ * @brief - called by generic request
+ *
+ * Called when current block i/o read is finished, restart block i/o or send the CSW.
+ *
+ * @param bh -
+ * @param uptodate -
+ * @return 0
+ */
+#if defined(LINUX26)
+static int msc_data_written(struct bio *bio, unsigned bytes_done,int err)
+{
+ struct usbd_function_instance *function_instance = bio->bi_private;
+ struct msc_private *msc = function_instance->privdata;
+ unsigned long flags;
+ u8 io_state;
+ if(bio->bi_size)
+ return 1;
+ /* printk(KERN_INFO "DATA WRITTEN bytes_done: %x\n", bytes_done);*/
+
+ TRACE_MSG4(MSC,"DATA WRITTEN bytes_done: %x err: %x state: %x io: %x",
+ bytes_done, err, msc->command_state, msc->io_state);
+
+ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba);
+
+ local_irq_save(flags);
+ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING;
+ msc->write_pending=0;
+ local_irq_restore(flags);
+
+ wake_up_interruptible(&msc->ioctl_wq);
+
+ /*
+ * verify that the I/O completed
+ */
+ if (err) {
+ TRACE_MSG0(MSC,"BLOCK READ FAILED");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
+ // XXX CHECKME
+ if (msc->rcv_urb_finished) {
+ usbd_free_urb(msc->rcv_urb_finished);
+ msc->rcv_urb_finished = NULL;
+ }
+ return 0;
+ }
+
+ /*
+ * If there was a rcv_urb that was not processed then we need
+ * to process it now. This is done by simply restarting msc_recv_out()
+ */
+ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) {
+ struct usbd_urb *rcv_urb = msc->rcv_urb_finished;
+ msc->rcv_urb_finished = NULL;
+ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED");
+ //printk(KERN_INFO "previuos rcv urb process\n");
+ msc_recv_out_blocks(rcv_urb);
+ }
+ /*
+ * DATA_IN mode and no more data to write
+ */
+ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) {
+ // finished, send CSW
+
+ if (msc->caseflag==12){
+ TRACE_MSG0(MSC,"DATA WRITTEN send CSW");
+ msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ return 0;
+ }
+
+ if (msc->caseflag==11){
+ usbd_halt_endpoint(function_instance, BULK_OUT);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PASSED;
+ return 0;
+ }
+ if (msc->caseflag==13){
+ usbd_halt_endpoint(function_instance, BULK_IN);
+ msc->data_transferred_in_bytes=msc->TransferLength_in_bytes;
+ msc->command_state = MSC_CBW_PHASE_ERROR;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+#else /* defined(LINUX26) */
+void msc_data_written(struct buffer_head *bh, int uptodate)
+{
+ struct usbd_function_instance *function_instance = bh->b_private;
+ struct msc_private *msc = function_instance->privdata;
+ unsigned long flags;
+ u8 io_state;
+
+ TRACE_MSG4(MSC,"DATA WRITTEN uptodate: %x b_state: %x state: %x io: %x",
+ uptodate, bh->b_state, msc->command_state, msc->io_state);
+
+ TRACE_MSG1(MSC,"DATA WRITTEN lba: %x", msc->lba);
+
+ local_irq_save(flags);
+ io_state = msc->io_state &= ~MSC_BLOCKIO_PENDING;
+ msc->write_pending=0;
+ local_irq_restore(flags);
+
+ wake_up_interruptible(&msc->ioctl_wq);
+
+ /*
+ * verify that the I/O completed
+ */
+ if (1 != uptodate) {
+ TRACE_MSG0(MSC,"BLOCK READ FAILED");
+ msc_start_sending_csw_failed (function_instance, SCSI_SENSEKEY_UNRECOVERED_READ_ERROR, msc->lba, USB_MSC_FAILED);
+ // XXX CHECKME
+ if (msc->rcv_urb_finished) {
+ usbd_free_urb(msc->rcv_urb_finished);
+ msc->rcv_urb_finished = NULL;
+ }
+ return;
+ }
+
+ /*
+ * If there was a rcv_urb that was not processed then we need
+ * to process it now. This is done by simply restarting msc_recv_out()
+ */
+ if ((io_state & MSC_RECV_FINISHED) && msc->rcv_urb_finished) {
+ struct usbd_urb *rcv_urb = msc->rcv_urb_finished;
+ msc->rcv_urb_finished = NULL;
+ TRACE_MSG0(MSC,"DATA WRITTEN RECV FINISHED");
+ msc_recv_out_blocks(rcv_urb);
+ }
+ /*
+ * DATA_IN mode and no more data to write
+ */
+ if (MSC_DATA_OUT_WRITE_FINISHED == msc->command_state) {
+ // finished, send CSW
+ TRACE_MSG0(MSC,"DATA WRITTEN send CSW");
+ msc_start_sending_csw(function_instance, USB_MSC_PASSED);
+ }
+}
+#endif /* defined(LINUX26) */
+extern struct usbd_interface_driver msc_interface_driver;
+
+
+/* USB Module init/exit ***************************************************** */
+
+void msc_io_exit_l24(void);
+
+/*! int msc_os_int_l24(struct usbd_function_intance * )
+ * @brief - called to initialize msc intance major and minor device number
+ *
+ * @param function_instance - pointer to this function instance
+ * @return 0
+ */
+int msc_os_init_l24(struct usbd_function_instance *function_instance)
+{
+ struct msc_private *msc = function_instance->privdata;
+ msc->major = MODPARM(major);
+ msc->minor = MODPARM(minor);
+ return 0;
+}
+
+extern void msc_global_init(void);
+/*! int msc_modinit ( )
+ * @brief - initialize msc global variables, and register interface function driver
+ *
+ *@return non-zero for error
+ */
+static int msc_modinit (void)
+{
+ int rc;
+
+ printk(KERN_INFO "Copyright (c) 2004 Belcarra Technologies; www.belcarra.com; sl@belcarra.com\n");
+ printk (KERN_INFO "%s vendor_id: %04x product_id: %04x major: %d minor: %d\n", __FUNCTION__,
+ MODPARM(vendor_id), MODPARM(product_id), MODPARM(major), MODPARM(minor));
+
+#ifndef OTG_C99
+#warning "C99"
+ msc_global_init();
+#endif
+ MSC = otg_trace_obtain_tag(NULL, "msc-if");
+
+ //if (vendor_id)
+ // msc_function_driver.idVendor = cpu_to_le16(vendor_id);
+ //if (product_id)
+ // msc_function_driver.idProduct = cpu_to_le16(product_id);
+
+ #if 0
+ init_waitqueue_head(&msc->msc_wq);
+ init_waitqueue_head(&msc->ioctl_wq);
+
+ msc->block_dev_state = DEVICE_EJECTED;
+
+
+ msc->command_state = MSC_READY;
+ msc->io_state = MSC_INACTIVE;
+ #endif
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ make_crc_table();
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+
+ TRACE_MSG3(MSC,"PAGE_SHIFT: %x PAGE_SIZE: %x %d", PAGE_SHIFT, PAGE_SIZE, PAGE_SIZE);
+
+
+ /* register as usb function driver
+ */
+ RETURN_EINVAL_IF(usbd_register_interface_function (&msc_interface_driver, "msc-if", NULL));
+
+ CATCH(error) {
+ otg_trace_invalidate_tag(MSC);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*! msc_modexit - module cleanup
+ */
+static void msc_modexit (void)
+{
+ #if 0
+ /* destroy control io interface
+ */
+ msc_io_exit_l24();
+
+ /* flush io and free page buffers
+ */
+ msc_close_blockdev(msc);
+ #endif
+ usbd_deregister_interface_function (&msc_interface_driver);
+
+#ifdef CONFIG_OTG_MSC_BLOCK_TRACE
+ free_crc_table();
+#endif /* CONFIG_OTG_MSC_BLOCK_TRACE */
+ otg_trace_invalidate_tag(MSC);
+}
+
+
+module_init (msc_modinit);
+module_exit (msc_modexit);
diff --git a/drivers/otg/functions/msc/msc-scsi.h b/drivers/otg/functions/msc/msc-scsi.h
new file mode 100644
index 000000000000..a36264d1a01a
--- /dev/null
+++ b/drivers/otg/functions/msc/msc-scsi.h
@@ -0,0 +1,837 @@
+/*
+ * 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
+ */
+/*
+ * otg/msc/msc-scsi.h - mass storage protocol library header
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc-scsi.h|20061218212925|54647
+ *
+ * Copyright(c) 2004-2006, Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+
+/*
+ *
+ * Documents
+ * Universal Serial Bus Mass Storage Class - Specification Overview
+ * Universal Serial Bus Mass Storage Class - Bulk-Only Transport
+ * T10/1240-D - Information technology - Reduced Block Commands
+ *
+ * Notes
+ *
+ * 1. Reduced Block Command set C.f. Table 2 RBC
+ *
+ * Command Support
+ * Name OpCode Fixed Removable Reference
+ * Format Unit 04h O O RBC
+ * Inquiry 12h M M SPC-2
+ * Mode Select (6) 15h M M SPC-2
+ * Mode Sense (6) 1Ah M M SPC-2
+ * Perisstent Reserve In 5Eh O O SPC-2
+ * Persistent Reserve Out 5Fh O O SPC-2
+ * Prevent/Allow Medium Removal 1Eh N/A M SPC-2
+ * Read (10) 28h M M RBC
+ * Read Capacity 25h M M RBC
+ * Reelase (6) 17h O O SPC-2
+ * Request Sense 03h O O SPC-2
+ * Reserve (6) 16h O O SPC-2
+ * Start Stop Unit 1Bh M M RBC
+ * Synchronize Cache 35h O O RBC
+ * Test Unit Ready 00h M M SPC-2
+ * Verify (10) 2Fh M M RBC
+ * Write (10) 2Ah M M RBC
+ * Write Buffer 3Bh M O SPC-2
+ *
+ * 2. Other commands seen?
+ * Sh MV FS
+ * SCSI_REZERO_UNIT 0x01 no
+ * SCSI_READ_6 0x08 yes
+ * SCSI_WRITE_6 0x0a yes
+ * SCSI_SEND_DIAGNOSTIC 0x1d no yes
+ * SCSI_READ_FORMAT_CAPACITY 0x23 yes yes
+ * SCSI_WRITE_AND_VERIFY 0x2e no
+ * SCSI_SEEK_10 0x2b no
+ * SCSI_MODE_SELECT_10 0x55 no yes yes
+ * SCSI_READ_12 0xa8 no yes
+ * SCSI_WRITE_12 0xaa no yes
+ *
+ *
+ * 3. Status - C.f. Chapter 5 - RBC
+ *
+ * Check Condition 02h
+ *
+ *
+ * 4. Sense Keys - C.f. Chapter 5 - RBC
+ *
+ * Not Ready 02h
+ * Media Error 03h
+ * Illegal Request 05h
+ * Unit Attention 06h
+ *
+ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC
+ *
+ * Logical Unit Not Ready 04h
+ * Logical Unit Not Ready, Format in Progress 04h,04h
+ * Invalid Command Operation Code 20h
+ * Invalide Field in CDB 24h
+ * Format Command Failed 31h,01h
+ * Status Notification / Power Management Class Event 38h,02h
+ * Status Notification / Media Class Event 38h,04h
+ * Status Notification / Device Busy Class Event 38h,06h
+ * Low Power Condition Active 5Eh,00
+ * Power Condition Change to Active 5Eh,41h
+ * Power Condition Change to Idle 5Eh,42h
+ * Power Condition Change to Standby 5Eh,43h
+ * Power Condition Change to Sleep 5Eh,45h
+ * Power Condition Change to Device Control 5Eh,47h
+ *
+ * 6. ASCQ - C.f. Chapter 5 - RBC
+ *
+ *
+ *
+ * 7. Sense Keys C.f. Chapter 5 - RBC
+ *
+ * Command Status Sense Key ASC/ASCQ
+ *
+ * 5.1 Format Unit
+ * Format progress Check Condition Not Ready, Logical Unit Note Ready, Format in Progress
+ * Sueccsful completion Check Condition Unit Attention, Status Notification / Media Class Event
+ * Failure Check Condition Media error, Format Command Failed
+ *
+ * 5.2 Read (10)
+ *
+ * 5.3 Read Capacity
+ * No Media Check Condition Not Ready, Logical Unit Not Ready
+ *
+ * 5.4 Start Stop Unit
+ * Power Consumption Check Condition Illegal Request,Low Power Condition Active
+ *
+ * 5.5 Synchronize Cache Check Condition Illegal Request,Invalid Command Operation Code
+ *
+ * 5.6 Write (10
+ *
+ * 5.7 Verify
+ *
+ * 5.8 Mode
+ *
+ * 6.1 Inquiry
+ *
+ * 6.2 Mode Select (6)
+ * Cannot Save Check Condition Illegal Request,Invalid Field in CDB
+ *
+ * 6.3 Mode Sense (6)
+ *
+ * 6.4 Prevent Allow Medium Removal
+ *
+ * 6.5 Request Sense
+ *
+ * 6.6 Test Unit Ready
+ *
+ * 6.7 Write Buffer
+ *
+ * 7.1 Unit Attention
+ * Power Contition change Check Condition Unit Attention, Power Condition Change Notification
+ *
+ * 7.4.1 Event Status Sense
+ *
+ * Power Management Event Check Condition Unit Attention, Event Status Notification / Power Managment
+ * Media Class Event Check Condition Unit Attention, Event Status Notification / Media Class
+ * Device Busy Event Check Condition Unit Attention, Event Status Notification / Device Busy Class
+ *
+ * 7.4.6 Removable Medium Device Initial Response
+ * Ready Check Condition Unit Attention, Event Status Notification / Media Class Event
+ * Power Check Condition Unit Attention, Event Status Notification / Power Management Class Event
+ */
+
+#ifndef _MSCSCSIPROTO_H_
+#define _MSCSCSIPROTO_H_
+
+
+/*
+ * Class Specific Requests - C.f. MSC BO Chapter 3
+ */
+
+#define MSC_BULKONLY_RESET 0xff
+#define MSC_BULKONLY_GETMAXLUN 0xfe
+
+
+/*
+ * Class Code
+ */
+
+#define MASS_STORAGE_CLASS 0x08
+
+/*
+ * MSC - Specification Overview
+ *
+ * SubClass Codes - C.f MSC Table 2.1
+ */
+
+#define MASS_STORAGE_SUBCLASS_RBC 0x01
+#define MASS_STORAGE_SUBCLASS_SFF8020I 0x02
+#define MASS_STORAGE_SUBCLASS_QIC157 0x03
+#define MASS_STORAGE_SUBCLASS_UFI 0x04
+#define MASS_STORAGE_SUBCLASS_SFF8070I 0x05
+#define MASS_STORAGE_SUBCLASS_SCSI 0x06
+
+/*
+ * Protocol - C.f MSC Table 3.1
+ */
+
+#define MASS_STORAGE_PROTO_CBI_WITH_COMP 0x00
+#define MASS_STORAGE_PROTO_CBI_NO_COMP 0x01
+#define MASS_STORAGE_PROTO_BULK_ONLY 0x50
+
+/*
+ * SCSI Command
+*/
+
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_FORMAT_UNIT 0x04
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT 0x15 // aka MODE_SELECT_6
+#define SCSI_MODE_SENSE 0x1a // aka MODE_SENSE_6
+#define SCSI_START_STOP 0x1b
+#define SCSI_PREVENT_ALLOW_MEDIA_REMOVAL 0x1e
+#define SCSI_READ_FORMAT_CAPACITY 0x23
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ_10 0x28
+#define SCSI_WRITE_10 0x2a
+#define SCSI_VERIFY 0x2f
+
+#define SCSI_READ_6 0x08
+#define SCSI_WRITE_6 0x0a
+#define SCSI_RESERVE 0x16
+#define SCSI_RELEASE 0x17
+#define SCSI_SEND_DIAGNOSTIC 0x1d
+#define SCSI_SYNCHRONIZE_CACHE 0x35
+#define SCSI_MODE_SENSE_10 0x5a
+
+#define SCSI_REZERO_UNIT 0x01
+#define SCSI_REASSIGN_BLOCKS 0x07
+#define SCSI_COPY 0x18
+#define SCSI_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_WRITE_AND_VERIFY 0x2e
+#define SCSI_PREFETCH 0x34
+#define SCSI_READ_DEFECT_DATA 0x37
+#define SCSI_COMPARE 0x39
+#define SCSI_COPY_AND_VERIFY 0x3a
+#define SCSI_WRITE_BUFFER 0x3b
+#define SCSI_READ_BUFFER 0x3c
+#define SCSI_READ_LONG 0x3e
+#define SCSI_WRITE_LONG 0x3f
+#define SCSI_CHANGE_DEFINITION 0x40
+#define SCSI_WRITE_SAME 0x41
+#define SCSI_LOG_SELECT 0x4c
+#define SCSI_LOG_SENSE 0x4d
+#define SCSI_XD_WRITE 0x50
+#define SCSI_XP_WRITE 0x51
+#define SCSI_XD_READ 0x52
+#define SCSI_MODE_SELECT_10 0x55
+#define SCSI_RESERVE_10 0x56
+#define SCSI_RELEASE_10 0x57
+#define SCSI_MODE_SELECT_10 0x55
+#define SCSI_XD_WRITE_EXTENDED 0x80
+#define SCSI_REBUILD 0x81
+#define SCSI_REGENERATE 0x82
+
+#define SCSI_SEEK_10 0x2b
+#define SCSI_WRITE_AND_VERIFY 0x2e
+#define SCSI_WRITE_12 0xaa
+#define SCSI_READ_12 0xa8
+
+/*
+ * Private
+ */
+#define SCSI_PRIVATE_PCS 0xff
+
+/*
+ * SCSI Command Parameter
+ */
+
+#define CBW_SIGNATURE 0x43425355 /* USBC */
+#define CSW_SIGNATURE 0x53425355 /* USBS */
+
+#define PRODUCT_REVISION_LEVEL "1.00"
+
+/*
+ * Command Block Status Values - C.f MSC BO Table 5.3
+ */
+
+#define USB_MSC_PASSED 0x00 // good
+#define USB_MSC_FAILED 0x01 // bad
+#define USB_MSC_PHASE_ERROR 0x02 // we want to be reset
+
+#define NODATAPHASE 1
+#define HASDATAPHASE 0
+
+
+
+/*
+ * SCSI Sense
+ * SenseKey
+ * AdditionalSenseCode
+ * SenseCodeQualifier
+ */
+
+#define SCSI_ERROR_CURRENT 0x70
+#define SCSI_ERROR_DEFERRED 0x07
+
+/*
+ * SCSI Sense Keys
+ */
+
+#define SK_NO_SENSE 0x00
+#define SK_RECOVERED_ERROR 0x01
+#define SK_NOT_READY 0x02
+#define SK_MEDIA_ERROR 0x03
+#define SK_HARDWARE_ERROR 0x04
+#define SK_ILLEGAL_REQUEST 0x05
+#define SK_UNIT_ATTENTION 0x06
+#define SK_DATA_PROTECT 0x07
+#define SK_BLANK_CHECK 0x08
+#define SK_COPY_ABORTED 0x0a
+#define SK_ABORTED_COMMAND 0x0b
+#define SK_VOLUME_OVERFLOW 0x0d
+#define SK_MISCOMPARE 0x0e
+
+/*
+ * 5. ASC/ASCQ - C.f. Chapter 5 - RBC
+ */
+
+#define SK(SenseKey,ASC,ASCQ) ((SenseKey<<16) | (ASC << 8) | (ASCQ))
+
+#define SCSI_SENSEKEY_NO_SENSE SK(SK_NO_SENSE, 0x00,0x00) // 0x000000
+
+#define SCSI_FAILURE_PREDICTION_THRESHOLD_EXCEEDED SK(SK_RECOVERED_ERROR, 0x5d,0x00) // 0x015d00
+
+#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_READY SK(SK_NOT_READY, 0x04,0x00) // 0x020400
+#define SCSI_SENSEKEY_FORMAT_IN_PROGRESS SK(SK_NOT_READY, 0x04,0x04) // 0x020404
+#define SCSI_SENSEKEY_MEDIA_NOT_PRESENT SK(SK_NOT_READY, 0x3a,0x00) // 0x023a00
+
+#define SCSI_SENSEKEY_WRITE_ERROR SK(SK_MEDIA_ERROR, 0x0c,0x02) // 0x030c02
+#define SCSI_SENSEKEY_UNRECOVERED_READ_ERROR SK(SK_MEDIA_ERROR, 0x11,0x00) // 0x031100
+#define SCSI_FORMAT_COMMAND_FAILED SK(SK_MEDIA_ERROR, 0x31,0x01) // 0x033101
+
+#define SCSI_SENSEKEY_COMMUNICATION_FAILURE SK(SK_HARDWARE_ERROR, 0x08,0x00) // 0x040800
+
+#define SCSI_SENSEKEY_INVALID_COMMAND SK(SK_ILLEGAL_REQUEST, 0x20,0x00) // 0x052000
+#define SCSI_SENSEKEY_BLOCK_ADDRESS_OUT_OF_RANGE SK(SK_ILLEGAL_REQUEST, 0x21,0x00) // 0x052100
+#define SCSI_SENSEKEY_INVALID_FIELD_IN_CDB SK(SK_ILLEGAL_REQUEST, 0x24,0x00) // 0x052400
+#define SCSI_SENSEKEY_LOGICAL_UNIT_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x25,0x00) // 0x052500
+#define SCSI_SENSEKEY_SAVING_PARAMETERS_NOT_SUPPORTED SK(SK_ILLEGAL_REQUEST, 0x39,0x00) // 0x053900
+#define SCSI_MEDIA_REMOVAL_PREVENTED SK(SK_ILLEGAL_REQUEST, 0x53,0x02) // 0x055302
+
+#define SCSI_SENSEKEY_NOT_READY_TO_READY_CHANGE SK(SK_UNIT_ATTENTION, 0x28,0x00) // 0x062800
+#define SCSI_SENSEKEY_RESET_OCCURRED SK(SK_UNIT_ATTENTION, 0x29,0x00) // 0x062900
+
+#define SCSI_SENSEKEY_STATUS_NOTIFICATION_POWER_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x02) // 0x063802
+#define SCSI_SENSEKEY_STATUS_NOTIFICATION_MEDIA_CLASS SK(SK_UNIT_ATTENTION, 0x38,0x04) // 0x063804
+#define SCSI_SENSEKEY_STATUS_NOTIFICATION_DEVICE_BUSY SK(SK_UNIT_ATTENTION, 0x38,0x06) // 0x063806
+
+#define SCSI_SENSEKEY_LOW_POWER_CONDITION_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x00) // 0x065e00
+#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_ACTIVE SK(SK_UNIT_ATTENTION, 0x5e,0x41) // 0x065e41
+#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_IDLE SK(SK_UNIT_ATTENTION, 0x5e,0x42) // 0x065e42
+#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_STANDBY SK(SK_UNIT_ATTENTION, 0x5e,0x43) // 0x065e43
+#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_SLEEP SK(SK_UNIT_ATTENTION, 0x5e,0x45) // 0x065e45
+#define SCSI_SENSEKEY_POWER_CONDITION_CHANGE_TO_DEVICE SK(SK_UNIT_ATTENTION, 0x5e,0x47) // 0x065e47
+
+
+#define SCSI_SENSEKEY_WRITE_PROTECTED SK(SK_DATA_PROTECT, 0x27,0x00) // 0x072700
+
+
+/*
+ * Mode Page Code and Page Control
+ */
+#define SCSI_MODEPAGE_CONTROL_CURRENT 0x0
+#define SCSI_MODEPAGE_CONTROL_CHANGEABLE 0x1
+#define SCSI_MODEPAGE_CONTROL_DEFAULT 0x2
+#define SCSI_MODEPAGE_CONTROL_SAVED 0x3
+
+#define SCSI_MODEPAGE_UNIT_ATTENTION 0x00
+#define SCSI_MODEPAGE_ERROR_RECOVERY 0x01
+#define SCSI_MODEPAGE_DISCONNNECT_RECONNECT 0x02
+#define SCSI_MODEPAGE_FORMAT 0x03
+#define SCSI_MODEPAGE_RIGID_DRIVE_GEOMETRY 0x04
+#define SCSI_MODEPAGE_FLEXIBLE_DISK_PAGE 0x05 // check
+#define SCSI_MODEPAGE_VERIFY_ERROR_RECOVERY 0x07
+#define SCSI_MODEPAGE_CACHING 0x08
+#define SCSI_MODEPAGE_CONTROL_MODE 0x0a
+#define SCSI_MODEPAGE_NOTCH_AND_PARTITION 0x0c
+#define SCSI_MODEPAGE_POWER_CONDITION 0x0d
+#define SCSI_MODEPAGE_XOR 0x10
+#define SCSI_MODEPAGE_CONTROL_MODE_ALIAS 0x1a
+#define SCSI_MODEPAGE_REMOVABLE_BLOCK_ACCESS 0x1b// check
+#define SCSI_MODEPAGE_INFORMATION_EXCEPTIONS 0x1c
+#define SCSI_MODEPAGE_ALL_SUPPORTED 0x3f
+
+
+
+/*
+ * Command Block Wrapper / Command Status Wrapper
+ */
+
+/*! @struct msc_command_block_wrapper msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief Command Block Wrapper
+ */
+ struct msc_command_block_wrapper {
+ u32 dCBWSignature;
+ u32 dCBWTag;
+ u32 dCBWDataTransferLength;
+ u8 bmCBWFlags;
+ u8 bCBWLUN:4,
+ Reserved:4;
+ u8 bCBWCBLength:5,
+ Reserved2:3;
+ u8 CBWCB[16];
+} __attribute__((packed));
+
+/*! @struct msc_command_status_wrapper msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief Command Status Wrapper
+ */
+struct msc_command_status_wrapper {
+ u32 dCSWSignature;
+ u32 dCSWTag;
+ u32 dCSWDataResidue;
+ u8 bCSWStatus;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_inquiry_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief SCSI INQUIRY command wrapper
+ */
+struct msc_scsi_inquiry_command {
+ u8 OperationCode;
+ u8 EnableVPD:1,
+ Reserved1:4,
+ LogicalUnitNumber:3;
+ u8 PageCode;
+ u8 Reserved2;
+ u8 AllocationLength;
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+} __attribute__((packed));
+
+/* ! @struct msc_scsi_inquiry_data msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc scsi inquiry data wrapper
+ */
+struct msc_scsi_inquiry_data {
+ u8 PeripheralDeviceType:5,
+ PeripheralQaulifier:3;
+ u8 Reserved2:7,
+ RMB:1;
+ u8 ANSIVersion:3,
+ ECMAVersion:3,
+ ISOVersion:2;
+ u8 ResponseDataFormat:4,
+ Reserved3:4;
+ u8 AdditionalLength;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 VendorInformation[8];
+ u8 ProductIdentification[16];
+ u8 ProductRevisionLevel[4];
+} __attribute__((packed));
+
+/*! @struct msc_scsi_read_format_capacity_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc scsi read_format_capacity command wrapper
+ *
+ */
+struct msc_scsi_read_format_capacity_command {
+ u8 OperationCode;
+ u8 Reserved1:5,
+ LogicalUnitNumber:3;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u16 AllocationLength;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_read_format_capacity_data msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc scsi read_format_capacity data warpper
+ *
+ */
+
+struct msc_scsi_read_format_capacity_data {
+ struct{
+ u8 Reserved1;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 CapacityListLength;
+ } __attribute__((packed)) CapacityListHeader;
+
+ struct{
+ u32 NumberofBlocks;
+ u8 DescriptorCode:2,
+ Reserved1:6;
+ u8 BlockLength[3];
+ } __attribute__((packed)) CurrentMaximumCapacityDescriptor;
+
+} __attribute__((packed));
+
+/*! @struct msc_scsi_read_capacity_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc scsi read_capacity_command wrapper
+ *
+ */
+struct msc_scsi_read_capacity_command {
+ u8 OperationCode;
+ u8 RelAdr:1,
+ Reserved1:4,
+ LogicalUnitNumber:3;
+ u32 LogicalBlockAddress;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 PMI:1,
+ Reserved4:7;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_read_capacity_data msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief READ FORMAT CAPACITY DATA
+ */
+struct msc_scsi_read_capacity_data {
+ u32 LastLogicalBlockAddress;
+ u32 BlockLengthInBytes;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_request_sense_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief REQUEST SENSE
+ */
+struct msc_scsi_request_sense_command {
+ u8 OperationCode;
+ u8 Reserved1:5,
+ LogicalUnitNumber:3;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 AllocationLength;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+ u8 Reserved10;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_request_sense_data msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief REQUEST SENSE DATA
+ */
+struct msc_scsi_request_sense_data {
+ u8 ErrorCode:7,
+ Valid:1;
+ u8 Reserved1;
+ u8 SenseKey:4,
+ Reserved2:4;
+ u32 Information;
+ u8 AdditionalSenseLength;
+ u8 Reserved3[4];
+ u8 AdditionalSenseCode;
+ u8 AdditionalSenseCodeQualifier;
+ u8 Reserved4;
+ u8 Reserved5[3];
+} __attribute__((packed));
+
+/* @struct msc_scsi_read_10_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc scsi read_10 command wrapper
+ */
+struct msc_scsi_read_10_command {
+ u8 OperationCode;
+ u8 RelAdr:1,
+ Reserved1:2,
+ FUA:1,
+ DPO:1,
+ LogicalUnitNumber:3;
+ u32 LogicalBlockAddress;
+ u8 Reserved2;
+ u16 TransferLength;
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_mode_sense_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc_scsi_mode_sense_command wrapper
+ * MODE SENSE
+ */
+struct msc_scsi_mode_sense_command {
+ u8 OperationCode;
+ u8 Reserved1:3,
+ DBD:1,
+ Reserved2:1,
+ LogicalUnitNumber:3;
+ u8 PageCode:6,
+ PageControl:2; // PC
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u16 ParameterListLength;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+} __attribute__((packed));
+
+/*! @struct msc_mode_parameter_header msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief MODE PARAMETER HEADER
+ */
+struct msc_mode_parameter_header {
+ u8 ModeDataLength;
+ u8 MediumTypeCode;
+ u8 Reserved1:4,
+ DPOFUA:1,
+ Reserved2:2,
+ WriteProtect:1;
+ u8 Reserved3;
+} __attribute__((packed));
+
+/*! @struct msc_read_write_error_recovery_page msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief MODE READ WRITE ERROR RECOVERY PAGE
+ */
+struct msc_read_write_error_recovery_page{
+ u8 PageCode:6,
+ Reserved1:1,
+ PS:1;
+ u8 PageLength;
+ u8 DCR:1,
+ Reserved2:1,
+ PER:1,
+ Reserved3:1,
+ RC:1,
+ Reserved4:1,
+ Reserved5:1,
+ AWRE:1;
+ u8 ReadRetryCount;
+ u8 Reserved6[4];
+ u8 WriteRetryCount;
+ u8 Reserved7[3];
+} __attribute__((packed));
+
+/*! @struct msc_flexible_disk_page msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief FLEXIBLE DISK PAGE
+ */
+struct msc_flexible_disk_page {
+ u8 PageCode:6,
+ Reserved1:1,
+ PS:1;
+ u8 PageLength;
+ u16 TransferRate;
+ u8 NumberofHeads;
+ u8 SectorsperTrack;
+ u16 DataBytesperSector;
+ u16 NumberofCylinders;
+ u8 Reserved2[9];
+ u8 MotorOnDelay;
+ u8 MotorOffDelay;
+ u8 Reserved3[7];
+ u16 MediumRotationRate;
+ u8 Reserved4;
+ u8 Reserved5;
+} __attribute__((packed));
+
+/*! @struct msc_removable_block_access_capabilities_page msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief REMOVABLE BLOCK ACCESS CAPABILITIES PAGE
+ */
+struct msc_removable_block_access_capabilities_page {
+ u8 PageCode:6,
+ Reserved1:1,
+ PS:1;
+ u8 PageLength;
+ u8 Reserved2:6,
+ SRFP:1,
+ SFLP:1;
+ u8 TLUN:3,
+ Reserved3:3,
+ SML:1,
+ NCD:1;
+ u8 Reserved4[8];
+} __attribute__((packed));
+
+/*! @struct msc_timer_and_protect_page msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief TIMER AND PROTECT PAGE
+ */
+ struct msc_timer_and_protect_page {
+ u8 PageCode:6,
+ Reserved1:1,
+ PS:1;
+ u8 PageLength;
+ u8 Reserved2;
+ u8 InactivityTimeMultiplier:4,
+ Reserved3:4;
+ u8 SWPP:1,
+ DISP:1,
+ Reserved4:6;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+} __attribute__((packed));
+
+/*! @struct msc_mode_all_pages msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief MODE ALL PAGES
+ */
+struct msc_mode_all_pages {
+ struct msc_read_write_error_recovery_page ReadWriteErrorRecoveryPage;
+ struct msc_flexible_disk_page FlexibleDiskPage;
+ struct msc_removable_block_access_capabilities_page RemovableBlockAccessCapabilitiesPage;
+ struct msc_timer_and_protect_page TimerAndProtectPage;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_mode_sense_date msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief SCSI MODE SENSE DATA
+ */
+struct msc_scsi_mode_sense_data {
+ struct msc_mode_parameter_header ModeParameterHeader;
+ union{
+ struct msc_read_write_error_recovery_page ReadWriteErrorRecoveryPage;
+ struct msc_flexible_disk_page FlexibleDiskPage;
+ struct msc_removable_block_access_capabilities_page RemovableBlockAccessCapabilitiesPage;
+ struct msc_timer_and_protect_page TimerAndProtectPage;
+ struct msc_mode_all_pages ModeAllPages;
+ } __attribute__((packed)) ModePages;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_test_unit_ready_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief TEST UNIT READY
+ */
+struct msc_scsi_test_unit_ready_command {
+ u8 OperationCode;
+ u8 Reserved1:5,
+ LogicalUnitNumber:3;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+ u8 Reserved10;
+ u8 Reserved11;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_prevent_allow_media_removal_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief PREVENT-ALLOW MEDIA REMOVAL
+ */
+struct msc_scsi_prevent_allow_media_removal_command {
+ u8 OperationCode;
+ u8 Reserved1:5,
+ LogicalUnitNumber:3;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 Prevent:2,
+ Reserved4:6;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+ u8 Reserved10;
+ u8 Reserved11;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_start_stop_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief START-STOP UNIT
+ */
+struct msc_scsi_start_stop_command {
+ u8 OperationCode;
+ u8 IMMED:1,
+ Reserved1:4,
+ LogicalUnitNumber:3;
+ u8 Reserved2;
+ u8 Reserved3;
+ u8 Start:1,
+ LoEj:1,
+ Reserved4:2,
+ PowerConditions:4;
+ u8 Reserved5;
+ u8 Reserved6;
+ u8 Reserved7;
+ u8 Reserved8;
+ u8 Reserved9;
+ u8 Reserved10;
+ u8 Reserved11;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_write_10_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief WRITE 10 command wrapper
+ */
+struct msc_scsi_write_10_command {
+ u8 OperationCode;
+ u8 RelAdr:1,
+ Reserved1:2,
+ FUA:1,
+ DPO:1,
+ LogicalUnitNumber:3;
+ u32 LogicalBlockAddress;
+ u8 Reserved2;
+ u16 TransferLength;
+ u8 Reserved3;
+ u8 Reserved4;
+ u8 Reserved5;
+} __attribute__((packed));
+
+/*! @struct msc_scsi_verify_command msc-scsi.h "otg/functions/msc/msc-scsi.h"
+ *
+ * @brief msc_scsi VERIFY command wrapper
+ */
+struct msc_scsi_verify_command {
+ u8 OperationCode;
+ u8 RelAdr:1,
+ ByteChk:1,
+ Reserved1:1,
+ Reserved2:1,
+ DPO:1,
+ LogicalUnitNumber:3;
+ u32 LogicalBlockAddress;
+ u8 Reserved3;
+ u16 VerificationLength;
+ u8 Reserved4;
+ u8 Reserved5;
+ u8 Reserved6;
+} __attribute__((packed));
+
+#endif /* _MSCSCSIPROTO_H_ */
diff --git a/drivers/otg/functions/msc/msc.h b/drivers/otg/functions/msc/msc.h
new file mode 100644
index 000000000000..155a12f71fc0
--- /dev/null
+++ b/drivers/otg/functions/msc/msc.h
@@ -0,0 +1,182 @@
+/*
+ * 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
+ */
+/*
+ * otg/function/msc/msc.h - Mass Storage Class
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/msc.h|20061218212925|08467
+ *
+ * Copyright (c) 2003-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ * Tony Tang <tt@belcarra.com>
+ *
+ */
+/*!
+ * @defgroup MSCFunction Mass Storage Interface Function
+ * @ingroup InterfaceFunctions
+ */
+/*!
+ * @file otg/functions/msc/msc.h
+ * @brief Mass Storage Driver private defines
+ *
+ *
+ * @ingroup MSCFunction
+ */
+
+#ifndef MSC_H
+#define MSC_H 1
+
+extern otg_tag_t msc_fd_trace_tag;
+#define MSC msc_fd_trace_tag
+
+/*
+ * Command/Data/Status Flow
+ * C.f. 5 - Figure 1
+ */
+
+typedef enum msc_state {
+ MSC_READY,
+ MSC_DATA_OUT_WRITE,
+ MSC_DATA_OUT_WRITE_FINISHED,
+ MSC_DATA_IN_READ,
+ MSC_DATA_IN_READ_FINISHED,
+ MSC_STATUS,
+ MSC_QUERY,
+ MSC_WAITFOR_RESET,
+ MSC_CBW_PASSED,
+ MSC_CBW_PHASE_ERROR,
+ MSC_CBW_FAILED,
+ MSC_WAITFOR_CLEAR,
+ MSC_UNKNOWN,
+} msc_state_t;
+
+
+/*
+ * Device Transfer state
+ * C.F. Table 6.1
+ */
+
+typedef enum msc_device_state {
+ MSC_DEVICE_DN, // The device intends to transfer no data
+ MSC_DEVICE_DI, // The device intends to send data to the host
+ MSC_DEVICE_DO, // The device intents to received data from the host
+} msc_device_state_t;
+
+#define MSC_INACTIVE 0x0000
+#define MSC_BLOCKIO_PENDING 0x0001
+#define MSC_BLOCKIO_FINISHED 0x0002
+#define MSC_RECV_PENDING 0x0010
+#define MSC_RECV_FINISHED 0x0020
+#define MSC_SEND_PENDING 0x0040
+#define MSC_SEND_FINISHED 0x0080
+#define MSC_IOCTL_WAITING 0x0100 // there is an ioctl call waiting for I/O completion
+#define MSC_ABORT_IO 0x0200 // please abort current i/o
+
+#if 0
+struct SPC_inquiry_cdb {
+ u8 OperationCode; /* 12H */
+ u8 EnableVPD:1;
+ u8 CmdSupportData:1;
+ u8 Reserved0:6;
+ u8 PageCode;
+ u8 Reserved1;
+ u8 AllocationLen;
+ u8 Control;
+} __attribute__((packed));
+#endif
+/*! @struct msc_private msc.h "otg/functions/msc/msc.h"
+ *
+ * @brief msc private data wrapper
+ */
+struct msc_private {
+
+ unsigned char connected; // non-zero if connected to host (configured)
+
+ struct usbd_urb *rcv_urb_finished;
+
+#if defined(LINUX26)
+ struct bio read_bio;
+ struct bio_vec rbio_vec;
+ struct bio write_bio;
+ struct bio_vec wbio_vec;
+ unsigned int bytes_done;
+ int err;
+
+#else /* defined(LINUX26) */
+ struct buffer_head read_bh;
+ struct buffer_head write_bh;
+ int uptodate;
+#endif /* defined(LINUX26) */
+
+ u8 read_pending;
+ u8 write_pending;
+ u8 caseflag;
+
+ msc_device_state_t device_state;
+ msc_state_t command_state; // current command state
+ u16 io_state; // current IO state
+ u8 endpoint_state; // Bulk in and Bulk out state bit 0 IN, bit 1 OUT. The rest are reserved
+
+ struct msc_command_block_wrapper command;
+
+ u32 lba; // next lba to read/write from
+ u32 transfer_blocks;
+ u32 TransferLength_in_blocks; // amount of transfer remaining
+ u32 TransferLength_in_bytes; // amount of transfer remaining
+ u32 data_transferred_in_bytes; // amount of data actually transferred
+
+ int major;
+ int minor;
+ //kdev_t dev;
+ dev_t dev;
+ struct gendisk *disk;
+ struct block_device *bdev;
+ u32 block_size;
+ u32 capacity;
+ u32 max_blocks;
+
+
+
+ wait_queue_head_t msc_wq;
+ wait_queue_head_t ioctl_wq;
+
+ u32 status;
+ u32 block_dev_state;
+ u32 sensedata;
+ u32 info;
+};
+
+
+/*
+ * MSC Configuration
+ *
+ * Endpoint, Class, Interface, Configuration and Device descriptors/descriptions
+ */
+
+#define BULK_OUT 0x00
+#define BULK_IN 0x01
+#define ENDPOINTS 0x02
+
+/* endpoint state*/
+
+#define BULK_IN_HALTED 0x01
+#define BULK_OUT_HALTED 0x02
+
+
+extern struct usbd_function_operations function_ops;
+extern struct usbd_function_driver function_driver;
+
+
+#endif
diff --git a/drivers/otg/functions/msc/otg-config.h b/drivers/otg/functions/msc/otg-config.h
new file mode 100644
index 000000000000..56dbcb0044b1
--- /dev/null
+++ b/drivers/otg/functions/msc/otg-config.h
@@ -0,0 +1,71 @@
+/*
+ * 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
+ */
+/*
+ *
+ * @(#) balden@belcarra.com|otg/functions/msc/otg-config.h|20060419204258|23307
+ *
+ */
+
+
+/*
+ * tristate " Mass Storage Function"
+ */
+/* #define CONFIG_OTG_MSC */
+
+/*
+ * hex "VendorID (hex value)"
+ * default "0x15ec"
+ */
+#define CONFIG_OTG_MSC_VENDORID 0x15ec
+
+/*
+ * hex "ProductID (hex value)"
+ * default "0xf006"
+ */
+#define CONFIG_OTG_MSC_PRODUCTID 0xf006
+
+/*
+ * hex "bcdDevice (binary-coded decimal)"
+ * default "0x0100"
+ */
+#define CONFIG_OTG_MSC_BCDDEVICE 0x0100
+
+/*
+ * string "iManufacturer (string)"
+ * default "Belcarra"
+ */
+#define CONFIG_OTG_MSC_MANUFACTURER "Belcarra"
+
+/*
+ * string "iProduct (string)"
+ * default "Mass Storage Class - Bulk Only"
+ */
+#define CONFIG_OTG_MSC_PRODUCT_NAME "Mass Storage Class - Bulk Only"
+
+/*
+ * string "MSC Bulk Only iInterface (string)"
+ * default "MSC BO Data Intf"
+ */
+#define CONFIG_OTG_MSC_INTF "MSC BO Data Intf"
+
+/*
+ * string "Data Interface iConfiguration (string)"
+ * default "MSC BO Configuration"
+ */
+#define CONFIG_OTG_MSC_DESC "MSC BO Configuratino"
+
+/*
+ * bool " MSC Tracing"
+ * default n
+ */
+#define CONFIG_OTG_MSC_REGISTER_TRACE
diff --git a/drivers/otg/functions/msc/trace.c b/drivers/otg/functions/msc/trace.c
new file mode 100644
index 000000000000..02251351c1b2
--- /dev/null
+++ b/drivers/otg/functions/msc/trace.c
@@ -0,0 +1,347 @@
+/*
+ * 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
+ */
+/*
+ * otg/msc_fd/trace.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/msc/trace.c|20070425221028|00673
+ *
+ * Copyright (c) 2003-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+
+#include <asm/atomic.h>
+#include <asm/io.h>
+
+#include <linux/proc_fs.h>
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/cache.h>
+
+
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+#include <usbp-chap9.h>
+#include <usbp-mem.h>
+#include <usbp-func.h>
+#include "msc-scsi.h"
+#include "msc.h"
+#include "trace.h"
+
+
+static struct msc_private *msc_private;
+int msc_trace_first;
+int msc_trace_next;
+msc_trace_t *msc_traces;
+
+extern int msc_interrupts;
+
+
+#if defined(CONFIG_OTG_MSC_REGISTER_TRACE) && defined(CONFIG_PROC_FS) || defined(_OTG_DOXYGEN)
+
+msc_trace_t *MSC_TRACE_NEXT(msc_trace_types_t msc_trace_type)
+{
+ msc_trace_t *p;
+
+ p = msc_traces + msc_trace_next;
+
+ if (msc_private) {
+ p->ticks = usbd_ticks(msc_private->function);
+ p->sofs = usbd_framenum(msc_private->function);
+ }
+ p->interrupts = msc_interrupts;
+ p->msc_trace_type = msc_trace_type;
+
+ msc_trace_next++;
+ msc_trace_next = (msc_trace_next == TRACE_MAX) ? 0 : msc_trace_next;
+
+ if (msc_trace_next == msc_trace_first) {
+ msc_trace_first++;
+ msc_trace_first = (msc_trace_first == TRACE_MAX) ? 0 : msc_trace_first;
+ }
+
+ return p;
+}
+
+/* Proc Filesystem *************************************************************************** */
+
+/* *
+ * msc_trace_proc_read - implement proc file system read.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Standard proc file system read function.
+ */
+static ssize_t msc_trace_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ int index;
+ int oindex;
+ int previous;
+
+ MOD_INC_USE_COUNT;
+ // get a page, max 4095 bytes of data...
+ if (!(page = get_free_page (GFP_KERNEL))) {
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+
+ len = 0;
+ oindex = index = (*pos)++;
+
+ if (index == 0)
+ len += sprintf ((char *) page + len, " Index Ints Ticks\n");
+
+
+ index += msc_trace_first;
+ if (index >= TRACE_MAX)
+ index -= TRACE_MAX;
+
+ previous = (index) ? (index - 1) : (TRACE_MAX - 1);
+
+
+ if (
+ ((msc_trace_first < msc_trace_next) && (index >= msc_trace_first) && (index < msc_trace_next)) ||
+ ((msc_trace_first > msc_trace_next) && ((index < msc_trace_next) || (index >= msc_trace_first)))
+ )
+ {
+
+ u64 ticks = 0;
+
+ msc_trace_t *p = msc_traces + index;
+ unsigned char *cp;
+ unsigned int *ip;
+ int skip = 0;
+
+ if (oindex > 0) {
+ msc_trace_t *o = msc_traces + previous;
+
+ if (o->ticks)
+ ticks = (p->ticks > o->ticks) ? (p->ticks - o->ticks) : (o->ticks - p->ticks) ;
+
+ if (o->interrupts != p->interrupts)
+ skip++;
+
+ }
+
+ //printk(KERN_INFO"index: %d interrupts: %d\n", index, p->interrupts);
+ len += sprintf ((char *) page + len, "%s%6d %8d ", skip?"\n":"", index, p->interrupts);
+
+ if (ticks > 1024*1024)
+ len += sprintf ((char *) page + len, "%8dM ", ticks>>20);
+ else
+ len += sprintf ((char *) page + len, "%8d ", ticks);
+
+ len += sprintf ((char *) page + len, "%6d ", (int)p->sofs);
+
+ switch (p->msc_trace_type) {
+ case msc_trace_msg:
+ len += sprintf ((char *) page + len, " -- ");
+ len += sprintf ((char *) page + len, p->trace.msg.msg);
+ break;
+
+ case msc_trace_w:
+ len += sprintf ((char *) page + len, " --> ");
+ len += sprintf ((char *) page + len, "[%8x] W %s", p->trace.msg32.val, p->trace.msg32.msg);
+ break;
+
+ case msc_trace_r:
+ len += sprintf ((char *) page + len, "<-- ");
+ len += sprintf ((char *) page + len, "[%8x] R %s", p->trace.msg32.val, p->trace.msg32.msg);
+ break;
+
+ case msc_trace_msg32:
+ len += sprintf ((char *) page + len, " -- ");
+ len += sprintf ((char *) page + len, p->trace.msg32.msg, p->trace.msg32.val);
+ break;
+
+ case msc_trace_msg16:
+ len += sprintf ((char *) page + len, " -- ");
+ len += sprintf ((char *) page + len, p->trace.msg16.msg, p->trace.msg16.val0, p->trace.msg16.val1);
+ break;
+
+ case msc_trace_msg8:
+ len += sprintf ((char *) page + len, " -- ");
+ len += sprintf ((char *) page + len, p->trace.msg8.msg,
+ p->trace.msg8.val0, p->trace.msg8.val1, p->trace.msg8.val2, p->trace.msg8.val3);
+ break;
+
+ case msc_trace_setup:
+ cp = (unsigned char *)&p->trace.setup;
+ len += sprintf ((char *) page + len,
+ " -- request [%02x %02x %02x %02x %02x %02x %02x %02x]",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ break;
+
+ case msc_trace_recv:
+ case msc_trace_sent:
+ cp = (unsigned char *)&p->trace.sent;
+ len += sprintf ((char *) page + len,
+ "%s %s [%02x %02x %02x %02x %02x %02x %02x %02x]",
+ ( p->msc_trace_type == msc_trace_recv)?"<-- ":" -->",
+ ( p->msc_trace_type == msc_trace_recv)?"recv":"sent",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ break;
+ case msc_trace_rlba:
+ ip = (unsigned int *)&p->trace.ints;
+ len += sprintf ((char *) page + len,
+ "%s %s [%8x %08x]",
+ "<-- ", "rlba", ip[0], ip[1]);
+ break;
+ case msc_trace_slba:
+ case msc_trace_tlba:
+ ip = (unsigned int *)&p->trace.ints;
+ len += sprintf ((char *) page + len,
+ "%s %s [%8x %08x]", " -->",
+ ( p->msc_trace_type == msc_trace_tlba)?"tlba":"slba",
+ ip[0], ip[1]);
+ break;
+
+ case msc_trace_tag:
+ ip = (unsigned int *)&p->trace.ints;
+ len += sprintf ((char *) page + len,
+ "%s TAG: %8x FRAME: %03x", " -->",
+ ip[0], ip[1]);
+ break;
+
+ case msc_trace_sense:
+ ip = (unsigned int *)&p->trace.ints;
+ len += sprintf ((char *) page + len,
+ "%s SENSE: %06x INFO: %08x", " -->",
+ ip[0], ip[1]);
+ break;
+
+ case msc_trace_cbw:
+ len += sprintf ((char *) page + len, " --> ");
+ len += sprintf ((char *) page + len, "%s %02x", p->trace.msg32.msg, p->trace.msg32.val);
+ break;
+ }
+ len += sprintf ((char *) page + len, "\n");
+ }
+
+ if ((len > count) || (len == 0))
+ len = -EINVAL;
+ else if (len > 0 && copy_to_user (buf, (char *) page, len))
+ len = -EFAULT;
+
+ free_page (page);
+ MOD_DEC_USE_COUNT;
+ return len;
+}
+
+/* *
+ * msc_trace_proc_write - implement proc file system write.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Proc file system write function, used to signal monitor actions complete.
+ * (Hotplug script (or whatever) writes to the file to signal the completion
+ * of the script.) An ugly hack.
+ */
+static ssize_t msc_trace_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+ return count;
+}
+
+static struct file_operations msc_trace_proc_operations_functions = {
+ read:msc_trace_proc_read,
+ write:msc_trace_proc_write,
+};
+
+
+/**
+ * msc_trace_init
+ *
+ * Return non-zero if not successful.
+ */
+int msc_trace_init (char *name, struct msc_private *msc)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ if (!(msc_traces = vmalloc(sizeof(msc_trace_t) * TRACE_MAX))) {
+ printk(KERN_ERR"%s: malloc failed %p %d\n", __FUNCTION__, msc_traces, sizeof(msc_trace_t) * TRACE_MAX);
+ return -EINVAL;
+ }
+ memset(msc_traces, 0, sizeof(msc_trace_t) * TRACE_MAX);
+
+ {
+ struct proc_dir_entry *p;
+
+ // create proc filesystem entries
+ if ((p = create_proc_entry (name, 0, 0)) == NULL) {
+ printk(KERN_INFO"BITRACE PROC FS failed\n");
+ }
+ else {
+ p->proc_fops = &msc_trace_proc_operations_functions;
+ }
+ }
+ printk(KERN_INFO"%s: OK\n", __FUNCTION__);
+ msc_private = msc;
+ return 0;
+}
+
+/**
+ * udc_release_io - release UDC io region
+ */
+void msc_trace_exit (char *name)
+{
+ msc_private = NULL;
+ {
+ unsigned long flags;
+ local_irq_save (flags);
+ remove_proc_entry (name, NULL);
+ if (msc_traces) {
+ msc_trace_t *p = msc_traces;
+ msc_traces = NULL;
+ vfree(p);
+ }
+ local_irq_restore (flags);
+ }
+}
+
+
+#else
+int msc_trace_init (void)
+{
+ return 0;
+}
+
+void msc_trace_exit (char *)
+{
+ return;
+}
+#endif
diff --git a/drivers/otg/functions/network/Kconfig b/drivers/otg/functions/network/Kconfig
new file mode 100644
index 000000000000..f26cbd5996f2
--- /dev/null
+++ b/drivers/otg/functions/network/Kconfig
@@ -0,0 +1,261 @@
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/functions/network/Kconfig|20070531044942|29753
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+menu "OTG Network Function"
+ depends on OTG
+
+ config OTG_NETWORK
+
+ tristate "USBOTG Network Interface Function Driver"
+ depends on OTG
+ default OTG
+
+
+ menu "USBOTG Network Interface Function Driver options"
+ depends on OTG_NETWORK
+
+ #comment --
+ #comment "Network Device Product Information"
+
+ #config OTG_NETWORK_VENDORID
+ # depends on OTG && OTG_NETWORK
+ # default "0x15ec"
+ # ---help---
+ # The USB Peripheral Vendor ID. This should be set to your assigned
+ # USB Vendor ID (www.usb.org). The USB Host will use this and the
+ # Product ID to find and load an appropriate device driver.
+
+ #config OTG_NETWORK_PRODUCTID
+ # hex "ProductID (hex value)"
+ # depends on OTG && OTG_NETWORK
+ # default "0xf001"
+ # ---help---
+ # The USB Peripheral Product ID. This should be set to your a
+ # unique value for this product. The USB Host will use this and the
+ # Vendor ID to find and load an appropriate device driver.
+
+ #config OTG_NETWORK_BCDDEVICE
+ # hex "bcdDevice (binary-coded decimal)"
+ # depends on OTG && OTG_NETWORK
+ # default "0x0100"
+
+ #config OTG_NETWORK_MANUFACTURER
+ # string "iManufacturer (string)"
+ # depends on OTG && OTG_NETWORK
+ # default "Belcarra"
+ # ---help---
+ # This will be used as the iManufacturer string descriptor.
+
+ #config OTG_NETWORK_PRODUCT_NAME
+ # string "iProduct (string)"
+ # depends on OTG && OTG_NETWORK
+ # default "Belcarra Network Device"
+ # ---help---
+ # This will be used as the iProduct string descriptor.
+
+ comment --
+ comment "Network Protocols"
+
+ config OTG_NETWORK_EEM
+ bool 'Enable CDC/EEM'
+ default TRUE
+ ---help---
+ Implement CDC/EEM (Ethernet Emulation Model) support.
+ This is the simplest and most efficent protocol available.
+ It requires only Bulk IN and Bulk Out endpoints and can
+ implement data streaming. It can be used for any type of
+ network device.
+
+ config OTG_NETWORK_EEM_CRC
+ bool "Append 32bit CRC"
+ depends on OTG && OTG_NETWORK_EEM
+ default TRUE
+ ---help---
+ Setting this allows the host and device to verify that
+ all network frames have been successfully transferred.
+
+ config OTG_NETWORK_EEM_STREAM
+ bool "Streaming mode"
+ depends on OTG && OTG_NETWORK_EEM
+ default FALSE
+ ---help---
+ Setting this will tell the EEM driver to stream data as much as
+ possible. This usually will result in higher throughput but possibly
+ slightly higher latencies.
+
+ config OTG_NETWORK_EEM_ZLE
+ bool "Use ZLE not ZLP"
+ depends on OTG && OTG_NETWORK_EEM
+ default FALSE
+ ---help---
+ Setting this will force the use of ZLE (Zero Length EEM packet)
+ instead of ZLP (Zero Length Packets) when a short packet is required
+ to terminate a transfer.
+
+ config OTG_NETWORK_ECM
+ bool 'Enable CDC/ECM networking'
+ default FALSE
+ ---help---
+ ECM implements the USB CDC ECM Class Specification Ethernet
+ Class Model to support infra-structure devices. This is the
+ older style of CDC networking over USB and requires 2 BULK
+ (IN and OUT) endpoints, an Interrupt endpoint and the device
+ must properly support SET INTERFACE.
+
+ comment --
+ comment "Older Network Protocols"
+
+ config OTG_NETWORK_DEPRECATED
+ bool "Enable older network protocols"
+ default FALSE
+ ---help---
+ Allow older protocols to be used.
+
+ config OTG_NETWORK_BLAN
+ bool 'Enable MDLM-BLAN networking'
+ depends on OTG && OTG_NETWORK_DEPRECATED
+ default FALSE
+ ---help---
+ BLAN supports non-infrastructure devices in virtual
+ bridged network environment. This protocol is deprecated.
+
+ config OTG_NETWORK_BLAN_CRC
+ bool "Append 32bit CRC"
+ depends on OTG && OTG_NETWORK_BLAN
+ default TRUE
+ ---help---
+ Setting this allows the host and device to verify that
+ all network frames have been successfully transferred.
+
+ config OTG_NETWORK_BLAN_AUTO_CONFIG
+ bool "Support Vendor Requests to configure the network interface"
+ depends on OTG && OTG_NETWORK_BLAN
+ default TRUE
+ ---help---
+ The driver will automatically configure the network interface
+ based on the IPADDR sent from the host to the device during
+ enumeration. This eliminates the need for hotplug.
+
+ config OTG_NETWORK_BLAN_NONBRIDGED
+ bool "Act as infrastructure device"
+ depends on OTG && OTG_NETWORK_BLAN
+ default FALSE
+ ---help---
+ Normally MDLM-BLAN is used to support smart devices in a virtual
+ network LAN implemented over USB with the USB Host acting as
+ a bridge between itself and all similiar devices. This option
+ tells the host to instead treat this as an infrastructure device
+ and not participate in the bridge.
+
+ config OTG_NETWORK_SAFE
+ bool 'Enable MDLM-SAFE networking'
+ depends on OTG && OTG_NETWORK_DEPRECATED
+ default FALSE
+ ---help---
+ SAFE supports infrastructure devices but does not
+ require support for SET INTERFACE or interrupt endpoints.
+ This protocol is deprecated.
+
+ config OTG_NETWORK_SAFE_CRC
+ bool "Append 32bit CRC"
+ depends on OTG && OTG_NETWORK_SAFE
+ default TRUE
+ ---help---
+ Setting this allows the host and device to verify that
+ all network frames have been successfully transferred.
+
+ config OTG_NETWORK_BASIC
+ bool 'Enable Basic network'
+ depends on OTG && OTG_NETWORK_DEPRECATED
+ default FALSE
+ ---help---
+ Implement a very simple network configuration
+ with a single data interface. This protocol is deprecated.
+
+ config OTG_NETWORK_BASIC2
+ bool 'Enable Basic2 network'
+ depends on OTG && OTG_NETWORK_DEPRECATED
+ default FALSE
+ ---help---
+ Implement a very simple network configuration with
+ two interfaces. This protocol is deprecated.
+
+
+ #config OTG_NETWORK_START_SINGLE
+ # bool " Start Single Urb Test"
+ # depends on OTG && OTG_NETWORK
+ # default n
+ # ---help---
+ # Used for testing, will not allow multiple receive urbs
+ # to be queued. This will generally slow transfer speeds
+ # and is used to test bus interface drivers operate properly
+ # when there is no receive urb queued.
+
+ #config OTG_NETWORK_EP0TEST
+ # bool " EP0 Test"
+ # depends on OTG && OTG_NETWORK
+ # default n
+ # ---help---
+ # Used for testing, this will change the product string to
+ # a string that is a multiple of the ep0 packetsize. This
+ # can be used to verify that the bus interface driver
+ # properly sends a ZLP after a string.
+
+ comment --
+ comment "Linux Network Options)"
+ config OTG_NETWORK_HOTPLUG
+ bool "Enable additional network hotplug support"
+ depends on OTG && HOTPLUG && OTG_NETWORK
+ default FALSE
+ ---help---
+ Enable additional Hotplug support. The network_fd agent script
+ will be called with ACTION=attach or ACTION=detach when the
+ device is configured or de-configured.
+
+ config OTG_NETWORK_HOTPLUG_PATH
+ string "Pathname of master hotplug program"
+ depends on OTG && HOTPLUG && OTG_NETWORK && OTG_NETWORK_HOTPLUG
+ default "/usr/sbin/hotplug"
+
+
+if (OTG != 'y')
+ comment --
+ comment "Network Device Type and Address configuration (testing)"
+ config OTG_NETWORK_TESTING
+ bool "Enable auto-config for testing"
+ depends on OTG && OTG_NETWORK_EEM
+ default FALSE
+ ---help---
+ Allow older protocols to be used.
+
+
+ config OTG_NETWORK_INFRASTRUCTURE
+ bool 'Infrastructure Device'
+ depends on OTG_NETWORK_TESTING
+ default FALSE
+ ---help---
+ The USB Peripheral is implementing networking as an
+ infrastructure device. More specifically a device
+ that may bridge network frames to another network.
+ This will enable a RARPD reply to requests from the
+ USB Host for a MAC address (for the host to use.)
+
+ config OTG_NETWORK_RARPD_AUTO_CONFIG
+ bool "Automatically configure the network interface using RARPD"
+ depends on OTG_NETWORK_TESTING
+ default TRUE
+ ---help---
+ The driver will automatically configure the network interface
+ using RARPD. RARPD is an extension to RARP that allows the
+ USB Host and USB Peripheral to determine if link level configuration
+ of MAC addresses.
+
+ config OTG_NETWORK_RFC868_AUTO_CONFIG
+ bool "Set the USB Peripheral System time using RFC868 Time Protocol (UDP)"
+ depends on OTG_NETWORK_TESTING
+ default TRUE
+ ---help---
+ The driver will automatically set the system time
+ using Time of Day (UDP) Service (RFC868.)
+endif
+ endmenu
+endmenu
diff --git a/drivers/otg/functions/network/Makefile b/drivers/otg/functions/network/Makefile
new file mode 100644
index 000000000000..ec14e417bfce
--- /dev/null
+++ b/drivers/otg/functions/network/Makefile
@@ -0,0 +1,25 @@
+#
+# Network Function Driver
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/functions/network/Makefile-l26|20070509205304|55506
+#
+# Copyright (c) 2002-2005 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+network_if-objs := net-l24-os.o net-fd.o blan-if.o basic-if.o basic2-if.o ecm-if.o eem-if.o safe-if.o fermat.o net-ip.o
+
+obj-$(CONFIG_OTG_NETWORK) += network_if.o
+
+obj-y += net-l24-fix.o
+
+OTG=$(TOPDIR)/drivers/otg
+NETWORKD=$(OTG)/functions/network
+OTGCORE_DIR=$(OTG)/otgcore
+EXTRA_CFLAGS += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
+EXTRA_CFLAGS_nostdinc += -I$(NETWORKD) -I$(OTG) -Wno-unused -Wno-format -I$(OTGCORE_DIR)
+
+
+
+# Link rules for multi-part drivers.
+
+#network_fd.o: $(network_fd-objs)
+# $(LD) -r -o $@ $(network_fd-objs)
diff --git a/drivers/otg/functions/network/basic-if.c b/drivers/otg/functions/network/basic-if.c
new file mode 100644
index 000000000000..5b74c952aa2b
--- /dev/null
+++ b/drivers/otg/functions/network/basic-if.c
@@ -0,0 +1,174 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/basic.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/basic-if.c|20070220211521|39075
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/basic-if.c
+ * @brief This file implements the required descriptors to implement
+ * a basic network device with a single interface.
+ *
+ * The BASIC network driver implements a very simple descriptor set.
+ * A single interface with two BULK data endpoints and a optional
+ * INTERRUPT endpoint.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+
+#define NTT network_fd_trace_tag
+
+#if defined(CONFIG_OTG_NETWORK_BASIC) || defined(_OTG_DOXYGEN)
+
+/* USB BASIC Configuration ******************************************************************** */
+
+/*! Data Alternate Interface Description List
+ */
+static struct usbd_alternate_description basic_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - MDLM/BLAN",
+ .bInterfaceClass = LINEO_CLASS,
+ .bInterfaceSubClass = LINEO_SUBCLASS_BASIC_NET,
+ .bInterfaceProtocol = LINEO_BASIC_NET_CRC,
+ .endpoints = ENDPOINTS,
+ .endpoint_index = net_fd_endpoint_index,
+ },
+};
+
+
+/* BASIC Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+struct usbd_interface_description basic_interfaces[] = {
+ {
+ .alternates = sizeof (basic_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = basic_data_alternate_descriptions,
+ },
+};
+
+
+/* ********************************************************************************************* */
+
+/*! int basic_fd_function_enable( struct usbd_function_instance)
+ * @brief -enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance - pointer to this function intance
+ * @return 0 for success
+ */
+
+int basic_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ return net_fd_function_enable(function_instance,
+ network_blan, net_fd_recv_urb_mdlm,
+ net_fd_start_xmit_mdlm,
+ net_fd_start_recv_mdlm, 0
+ );
+}
+
+
+/* ********************************************************************************************* */
+/*! basic_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations basic_fd_function_ops = {
+ .function_enable = basic_fd_function_enable,
+ .function_disable = net_fd_function_disable,
+
+ .device_request = net_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .endpoint_cleared = net_fd_endpoint_cleared,
+ .set_configuration = net_fd_set_configuration,
+ .set_interface = NULL,
+ .reset = net_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+
+/*! function driver description
+ */
+struct usbd_interface_driver basic_interface_driver = {
+ .driver = {
+ .name = "net-basic-if",
+ .fops = &basic_fd_function_ops, },
+ .interfaces = sizeof (basic_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = basic_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = net_fd_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .iFunction = "Belcarra USBLAN - Basic",
+};
+
+/* ********************************************************************************************* */
+
+/*!int basic_mod_init()
+ * @brief initialize interface module throught registering driver
+ * @return int
+ */
+int basic_mod_init (void)
+{
+ return usbd_register_interface_function (&basic_interface_driver, "net-basic-if", NULL);
+}
+
+/*!
+* @brief basic_mod_exit
+ * @return none
+ */
+void basic_mod_exit(void)
+{
+ usbd_deregister_interface_function (&basic_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_BASIC */
+/*!
+ * @brief basic_mod_init
+ * @return int
+ */
+int basic_mod_init (void)
+{
+ return 0;
+}
+
+/*!
+ * @brief basic_mod_exit
+ * @return none
+ */
+void basic_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_BASIC */
diff --git a/drivers/otg/functions/network/basic2-if.c b/drivers/otg/functions/network/basic2-if.c
new file mode 100644
index 000000000000..053b84f793c7
--- /dev/null
+++ b/drivers/otg/functions/network/basic2-if.c
@@ -0,0 +1,185 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/basic2.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/basic2-if.c|20070220211521|10696
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/basic2-if.c
+ * @brief This file implements the required descriptors to implement
+ * a basic network device with two interfaces.
+ *
+ * The BASIC2 network driver implements a very simple descriptor set.
+ * Two interfaces with two BULK data endpoints and a optional
+ * INTERRUPT endpoint.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+
+#define NTT network_fd_trace_tag
+
+#if defined(CONFIG_OTG_NETWORK_BASIC) || defined(_OTG_DOXYGEN)
+
+/* USB BASIC Configuration ******************************************************************** */
+
+/*! Data Alternate Interface Description List
+ */
+static struct usbd_alternate_description basic2_nodata_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - Basic2",
+ .bInterfaceClass = LINEO_CLASS,
+ .bInterfaceSubClass = LINEO_SUBCLASS_BASIC_NET,
+ .bInterfaceProtocol = 0,
+ .endpoints = 1,
+ .endpoint_index = cdc_int_endpoint_index,
+ },
+};
+/*! @var struct usbd_alternate_description basic2_data_alternate_descriptions
+ * @brief an array of alternate descriptions
+ *
+ */
+ static struct usbd_alternate_description basic2_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - Basic2 Data",
+ .bInterfaceClass = LINEO_CLASS,
+ .bInterfaceSubClass = LINEO_SUBCLASS_BASIC_NET,
+ .bInterfaceProtocol = LINEO_BASIC_NET_CRC,
+ .endpoints = 2,
+ .endpoint_index = cdc_data_endpoint_index,
+ },
+};
+
+
+/* BASIC Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+struct usbd_interface_description basic2_interfaces[] = {
+ {
+ .alternates = sizeof (basic2_nodata_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = basic2_data_alternate_descriptions,
+ },
+ {
+ .alternates = sizeof (basic2_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = basic2_data_alternate_descriptions,
+ },
+};
+
+
+/* ********************************************************************************************* */
+
+/*! basic2_fd_function_enable - enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ */
+
+int basic2_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ return net_fd_function_enable(function_instance,
+ network_blan, net_fd_recv_urb_mdlm,
+ net_fd_start_xmit_mdlm,
+ net_fd_start_recv_mdlm, 0
+ );
+}
+
+
+/* ********************************************************************************************* */
+/*! basic2_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations basic2_fd_function_ops = {
+ .function_enable = basic2_fd_function_enable,
+ .function_disable = net_fd_function_disable,
+
+ .device_request = net_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .endpoint_cleared = net_fd_endpoint_cleared,
+ .set_configuration = net_fd_set_configuration,
+ .set_interface = NULL,
+ .reset = net_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+
+/*! function driver description
+ */
+struct usbd_interface_driver basic2_interface_driver = {
+ .driver = {
+ .name = "net-basic2-if",
+ .fops = &basic2_fd_function_ops, },
+ .interfaces = sizeof (basic2_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = basic2_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = net_fd_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .iFunction = "Belcarra USBLAN - Basic2",
+};
+
+/* ********************************************************************************************* */
+
+/*!
+ * @brief basic2_mod_init
+ * @return none
+ */
+int basic2_mod_init (void)
+{
+ return usbd_register_interface_function (&basic2_interface_driver, "net-basic2-if", NULL);
+}
+
+/*!
+ * basic2_mod_exit
+ * @return void
+ */
+
+void basic2_mod_exit(void)
+{
+ usbd_deregister_interface_function (&basic2_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_BASIC */
+/*!
+ * @brief basic2_mod_init
+ * @return none
+ */
+int basic2_mod_init (void)
+{
+ return 0;
+}
+
+void basic2_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_BASIC */
diff --git a/drivers/otg/functions/network/blan-if.c b/drivers/otg/functions/network/blan-if.c
new file mode 100644
index 000000000000..54788c62fb4e
--- /dev/null
+++ b/drivers/otg/functions/network/blan-if.c
@@ -0,0 +1,1098 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/blan.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/blan-if.c|20070814215823|43564
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/blan-if.c
+ * @brief This file implements the required descriptors to implement
+ * a BLAN network device with a single interface.
+ *
+ * The BLAN network driver implements the BLAN protocol descriptors.
+ *
+ * The BLAN protocol is designed to support smart devices that want
+ * to create a virtual network between them host and other similiar
+ * devices.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+#include "net-os.h"
+
+#define NTT network_fd_trace_tag
+
+#define TRACE_VERBOSE 0
+#define TRACE_VERY_VERBOSE 0
+#undef DEBUG_CRC_SEEN
+
+#if defined(CONFIG_OTG_NETWORK_BLAN) || defined(_OTG_DOXYGEN)
+
+/* USB BLAN Configuration ******************************************************************** */
+
+/*
+ * BLAN Ethernet Configuration
+ */
+
+/*! @name Descriptors - Communication Interface Class descriptors
+ *
+ * @{
+ */
+static struct usbd_class_header_function_descriptor blan_class_1 = {
+ .bFunctionLength = 0x05, /* Length */
+ .bDescriptorType = USB_DT_CLASS_SPECIFIC,
+ .bDescriptorSubtype = USB_ST_HEADER,
+ .bcdCDC = __constant_cpu_to_le16(0x0110) /*Version */
+};
+static struct usbd_class_mdlm_descriptor blan_class_2 = {
+ .bFunctionLength = 0x15,
+ .bDescriptorType = USB_DT_CLASS_SPECIFIC,
+ .bDescriptorSubtype = USB_ST_MDLM,
+ .bcdVersion = __constant_cpu_to_le16(0x0100),
+ .bGUID = {
+ 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */
+ 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37, /* bGUID */ },
+};
+static struct usbd_class_blan_descriptor blan_class_3 = {
+ .bFunctionLength = 0x07,
+ .bDescriptorType = USB_DT_CLASS_SPECIFIC,
+ .bDescriptorSubtype = USB_ST_MDLMD,
+ .bGuidDescriptorType = 0x01,
+ .bmNetworkCapabilities = 0x00,
+ .bmDataCapabilities = 0x00,
+ .bPad = 0x00,
+};
+static struct usbd_class_ethernet_networking_descriptor blan_class_4 = {
+ .bFunctionLength = 0x0d,
+ .bDescriptorType = USB_DT_CLASS_SPECIFIC,
+ .bDescriptorSubtype = USB_ST_ENF,
+ .iMACAddress = 0x00,
+ .bmEthernetStatistics = 0x00,
+ .wMaxSegmentSize = 0x05ea, /* 1514 maximum frame size */
+ .wNumberMCFilters = 0x00,
+ .bNumberPowerFilters = 0x00 ,
+};
+static struct usbd_class_network_channel_descriptor blan_class_5 = {
+ .bFunctionLength = 0x07,
+ .bDescriptorType = USB_DT_CLASS_SPECIFIC,
+ .bDescriptorSubtype = USB_ST_NCT,
+ .bEntityId = 0,
+ .iName = 0,
+ .bChannelIndex = 0,
+ .bPhysicalInterface = 0,
+};
+
+
+static struct usbd_generic_class_descriptor *blan_comm_class_descriptors[] = {
+ (struct usbd_generic_class_descriptor *) &blan_class_1,
+ (struct usbd_generic_class_descriptor *) &blan_class_2,
+ (struct usbd_generic_class_descriptor *) &blan_class_3,
+ (struct usbd_generic_class_descriptor *) &blan_class_4,
+ (struct usbd_generic_class_descriptor *) &blan_class_5, };
+
+/*! Data Alternate Interface Description List
+ */
+static struct usbd_alternate_description blan_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - MDLM/BLAN",
+ .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS,
+ .bInterfaceSubClass = COMMUNICATIONS_MDLM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .classes = sizeof (blan_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = blan_comm_class_descriptors,
+ .endpoints = ENDPOINTS,
+ .endpoint_index = net_fd_endpoint_index,
+ },
+};
+
+/*! @} */
+/* Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+static struct usbd_interface_description blan_interfaces[] = {
+ {
+ .alternates = sizeof (blan_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = blan_alternate_descriptions,
+ },
+};
+
+/* ********************************************************************************************* */
+
+/*! net_fd_start_xmit_mdlm
+ * @brief - start sending a buffer
+ *
+ * @param function_instance - function instance pointer
+ * @param buffer
+ * @param len
+ * @param data
+ *
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_xmit_mdlm (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+ u32 crc;
+
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ int xmit_index = 0;
+ int endpoint_index = BULK_IN_A;
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ int xmit_index = (otg_atomic_read(&npd->xmit_urbs_started[0]) <= otg_atomic_read(&npd->xmit_urbs_started[1]))
+ ? 0 : 1;
+ int endpoint_index = (xmit_index) ? BULK_IN_B : BULK_IN_A;
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+
+ int in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, xmit_index, usbd_high_speed(function_instance));
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG8(NTT,"npd: %p flags: %04x len: %d endpoint_index: %d "
+ "xmit_index: %d xmit_started: %d %d in_pkt_sz: %d",
+ npd, npd->flags, len, endpoint_index, xmit_index, otg_atomic_read(&npd->xmit_urbs_started[0]),
+ otg_atomic_read(&npd->xmit_urbs_started[1]), in_pkt_sz);
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ /* allocate urb 5 bytes larger than required */
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_bulk ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ return -ENOMEM;
+ }
+
+ urb->actual_length = len;
+
+ /* copy and crc len bytes */
+ crc = crc32_copy(urb->buffer, buffer, len, CRC32_INIT);
+
+ if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) {
+ /* no longer in Kconfig - change undef to define to active padbyte */
+ #undef CONFIG_OTG_NETWORK_PADBYTE
+ #ifdef CONFIG_OTG_NETWORK_PADBYTE
+ // add a pad byte if required to ensure a short packet, usbdnet driver
+ // will correctly handle pad byte before or after CRC, but the MCCI driver
+ // wants it before the CRC.
+ crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc);
+ urb->actual_length++;
+ #else /* CONFIG_OTG_NETWORK_PADBYTE */
+ urb->flags |= USBD_URB_SENDZLP;
+ TRACE_MSG2(NTT,"setting ZLP: urb: %p flags: %x", urb, urb->flags);
+ #endif /* CONFIG_OTG_NETWORK_PADBYTE */
+ }
+ crc = ~crc;
+ urb->buffer[urb->actual_length++] = crc & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 8) & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 16) & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 24) & 0xff;
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT)
+ if (npd->fermat) fermat_encode(urb->buffer, urb->actual_length);
+ #endif
+
+ #else /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+ /* allocate urb with no buffer */
+ #ifdef CONFIG_OTG_NETWORK_XMIT_OS
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, 0, net_fd_urb_sent_bulk ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ return -ENOMEM;
+ }
+ urb->actual_length = len;
+ urb->buffer = buffer;
+ #else /* CONFIG_OTG_NETWORK_XMIT_OS */
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_bulk ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa);
+ return -ENOMEM;
+ }
+ urb->actual_length = len;
+ memcpy (urb->buffer, buffer, len);
+ #endif /* CONFIG_OTG_NETWORK_XMIT_OS */
+
+ urb->flags |= ((urb->actual_length % in_pkt_sz) == 0) ? USBD_URB_SENDZLP : 0;
+
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(NTT,"urb: %p buf: %p priv: %p", urb, data, urb->function_privdata);
+ urb->function_privdata = data;
+
+ otg_atomic_add(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_inc(&npd->xmit_urbs_started[xmit_index]);
+ if ((rc = usbd_start_in_urb (urb))) {
+
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+ urb->function_privdata = NULL;
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_dec(&npd->xmit_urbs_started[xmit_index]);
+ usbd_free_urb (urb);
+
+ return rc;
+ }
+ return 0;
+}
+
+/* ********************************************************************************************* */
+
+/*! net_fd_recv_urb_mdlm
+ * @brief callback to process a received URB
+ *
+ * @param urb - pointer to received urb
+ * @param rc - dummy parameter
+ * @return non-zero for failure.
+ */
+int net_fd_recv_urb_mdlm(struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ void *os_data = NULL;
+ void *os_buffer = NULL;
+ int crc_bad = 0;
+ int trim = 0;
+ int len;
+ u32 crc;
+ u32 temmp;
+ len = urb->actual_length;
+ trim = 0;
+
+ //TRACE_MSG2(NTT, "status: %d actual_length: %d", urb->status, urb->actual_length);
+ //RETURN_EINVAL_IF (urb->status == USBD_URB_OK);
+
+ os_data = urb->function_privdata;
+
+ //TRACE_MSG2(NTT, "os_data: %x os_buffer: %x", os_data, os_buffer);
+
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_PADAFTER)
+ {
+ /* This version simply checks for a correct CRC along the
+ * entire packet. Some UDC's have trouble with some packet
+ * sizes, this allows us to add pad bytes after the CRC.
+ */
+
+ u8 *src = urb->buffer;
+ int copied;
+
+ // XXX this should work, but the MIPS optimizer seems to get it wrong....
+ //copied = (len < urb->wMaxPacketSize) ? 0 : ((len / urb->wMaxPacketSize) - 1) * urb->wMaxPacketSize;
+
+ if (len < urb->wMaxPacketSize*2)
+ copied = 0;
+ else {
+ int pkts = ((len - urb->wMaxPacketSize) / urb->wMaxPacketSize);
+ copied = (pkts - 1) * urb->wMaxPacketSize;
+ }
+
+ len -= copied;
+ crc = CRC32_INIT;
+ for (; copied-- > 0 ; crc = COMPUTE_FCS (crc, *os_buffer++ = *src++));
+
+ for (; (len-- > 0) && (CRC32_GOOD != crc); crc = COMPUTE_FCS (crc, *os_buffer++ = *src++));
+
+ trim = len + 4;
+
+ if (CRC32_GOOD != crc) {
+ TRACE_MSG1(NTT,"AAA frame: %03x", urb->framenum);
+ THROW_IF(npd->seen_crc, crc_error);
+ }
+ else
+ npd->seen_crc = 1;
+ }
+ //#else /* defined(CONFIG_OTG_NETWORK_BLAN_PADAFTER) */
+ #elif defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ /*
+ * The CRC can be sent in two ways when the size of the transfer
+ * ends up being a multiple of the packetsize:
+ *
+ * |
+ * <data> <CRC><CRC><CRC><CRC>|<???> case 1
+ * <data> <NUL><CRC><CRC><CRC>|<CRC> case 2
+ * <data> <NUL><CRC><CRC><CRC><CRC>| case 3
+ * <data> <NUL><CRC><CRC><CRC>|<CRC> | case 4
+ * |
+ *
+ * This complicates CRC checking, there are four scenarios:
+ *
+ * 1. length is 1 more than multiple of packetsize with a trailing byte
+ * 2. length is 1 more than multiple of packetsize
+ * 3. length is multiple of packetsize
+ * 4. none of the above
+ *
+ * Finally, even though we always compute CRC, we do not actually throw
+ * things away until and unless we have previously seen a good CRC.
+ * This allows backwards compatibility with hosts that do not support
+ * adding a CRC to the frame.
+ *
+ */
+
+ // test if 1 more than packetsize multiple
+ if (1 == (len % urb->wMaxPacketSize)) {
+ u8 *cp = urb->buffer + len - 1 - 4;
+ // copy and CRC up to the packetsize boundary
+ crc = crc32_nocopy(urb->buffer, len - 1, CRC32_INIT);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(NTT,"A CRC nocopy: %08x %08x len: %d CRC: %02x %02x %02x %02x",
+ CRC32_GOOD, crc, len, cp[0], cp[1], cp[2], cp[3]);
+
+ // if the CRC is good then this is case 1
+ if (CRC32_GOOD != crc) {
+
+ crc = crc32_nocopy(urb->buffer + len - 1, 1, crc);
+
+ if (CRC32_GOOD != crc) {
+ //crc_errors[len%64]++;
+ TRACE_MSG3(NTT,"A CRC error %08x %08x %03x", CRC32_GOOD, crc, urb->framenum);
+ printk(KERN_INFO"%s: A CRC\n", __FUNCTION__);
+ npd->seen_crc_error = 1;
+ THROW_IF(npd->seen_crc, crc_error);
+ }
+ else
+ npd->seen_crc = 1;
+ }
+ else
+ npd->seen_crc = 1;
+
+ }
+ else {
+ u8 *cp = urb->buffer + len - 4;
+ crc = crc32_nocopy(urb->buffer, len, CRC32_INIT);
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(NTT,"B CRC nocopy: %08x %08x len: %d CRC: %02x %02x %02x %02x",
+ CRC32_GOOD, crc, len, cp[0], cp[1], cp[2], cp[3]);
+
+ if (CRC32_GOOD != crc) {
+ //crc_errors[len%64]++;
+ TRACE_MSG3(NTT,"B CRC error %08x %08x %03x", CRC32_GOOD, crc, urb->framenum);
+ if (TRACE_VERBOSE) {
+
+ TRACE_MSG2(NTT, "status: %d actual_length: %d", urb->status, urb->actual_length);
+
+ TRACE_NRECV(NTT, 32, urb->buffer);
+ TRACE_MSG0(NTT, "--");
+ TRACE_RECV(NTT, urb->actual_length, urb->buffer);
+ }
+ printk(KERN_INFO"%s: B CRC\n", __FUNCTION__);
+ npd->seen_crc_error = 1;
+ THROW_IF(npd->seen_crc, crc_error);
+ }
+ else
+ npd->seen_crc = 1;
+
+ // XXX shorten by 4 bytes?
+
+ }
+ // trim IFF we are paying attention to crc
+ if (npd->seen_crc)
+ trim = 4;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+ if (net_fd_recv_buffer(function_instance, urb->buffer, len, os_data, crc_bad, trim)) {
+ TRACE_MSG0(NTT, "FAILED");
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ }
+
+ // catch a simple error, just increment missed error and general error
+ CATCH(error) {
+ //TRACE_MSG4(NTT,"CATCH(error) urb: %p status: %d len: %d function: %p",
+ // urb, urb->status, urb->actual_length, function_instance);
+ // catch a CRC error
+ CATCH(crc_error) {
+ crc_bad = 1;
+ npd->seen_crc_error = 1;
+ }
+ }
+ return 0;
+}
+
+
+
+/* ********************************************************************************************* */
+int blan_start_recv(struct usbd_function_instance *function_instance);
+
+/*! net_fd_recv_urb2 - callback to process a received URB
+ *
+ * @param urb - pointer to copy of received urb,
+ * @param rc - receiving urb result code
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_recv_urb2(struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ int hs = usbd_high_speed(function_instance);
+ int endpoint_index = urb->endpoint_index;
+
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ int recv_index = 0;
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ int recv_index = (endpoint_index == BULK_OUT_A) ? 0 : 1;
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ int alloc_length = usbd_endpoint_transferSize(function_instance, endpoint_index, hs);
+ int status = urb->status;
+
+ void *os_data;
+ u8 *os_buffer;
+
+
+ if (TRACE_VERY_VERBOSE) {
+
+ TRACE_MSG4(NTT, "status: %d actual_length: %d bus status: %d device_state: %d",
+ urb->status, urb->actual_length,
+ usbd_get_device_status(function_instance), usbd_get_device_state(function_instance)
+ );
+
+ TRACE_NRECV(NTT, 32, urb->buffer);
+ TRACE_MSG0(NTT, "--");
+ TRACE_RECV(NTT, urb->actual_length, urb->buffer);
+ }
+
+
+ otg_atomic_dec(&npd->recv_urbs_started[recv_index]);
+
+ /* process the data */
+ if (urb->status == USBD_URB_OK)
+ npd->net_recv_urb(urb, rc);
+
+ if (urb->status == USBD_URB_CANCELLED)
+ net_os_dealloc_buffer(function_instance, urb->function_privdata, urb->buffer);
+
+ /* disconnect os_data buffer from urb */
+ urb->function_privdata = NULL;
+ urb->buffer = NULL;
+ urb->function_instance = NULL;
+ urb->status = USBD_URB_OK;
+ usbd_free_urb(urb);
+
+ if ((USBD_OK == usbd_get_device_status(function_instance)) &&
+ (STATE_CONFIGURED == usbd_get_device_state(function_instance))) {
+
+ blan_start_recv(function_instance);
+ }
+ else {
+ TRACE_MSG0(NTT, "NOT RESTARTING");
+ }
+
+ return 0;
+}
+
+int blan_start_recv_endpoint(struct usbd_function_instance *function_instance,
+ int endpoint_index, otg_atomic_t *recv_urbs_started, char *msg)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int i;
+ int hs = usbd_high_speed(function_instance);
+ int alloc_length = usbd_endpoint_transferSize(function_instance, endpoint_index, hs);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG4(NTT, "endpoint_index: %d recv_urbs_started: %d alloc_length: %d %s",
+ endpoint_index, otg_atomic_read(recv_urbs_started), alloc_length, msg);
+
+ while (otg_atomic_read(recv_urbs_started) < npd->max_recv_urbs ) {
+ u8 *os_buffer = NULL;
+ void *os_data = NULL;
+ struct usbd_urb *urb;
+
+ #ifdef DEBUG_CRC_SEEN
+ if (npd->seen_crc_error) {
+ TRACE_MSG0(NTT, "CRC ERROR NOT RESTARTING");
+ break;
+ }
+ #endif /* DEBUG_CRC_SEEN */
+
+ /* get os buffer - os_buffer is data, os_data is the os data structure */
+ os_data = net_os_alloc_buffer(function_instance, &os_buffer, alloc_length);
+
+ /* allocate urb with no buffer */
+ /*allocate urb without buffer */
+ urb = usbd_alloc_urb(function_instance, endpoint_index, 0, net_fd_recv_urb2);
+
+ /* start urb with buffer pointing at the os_buffer in os_data structure */
+ if (os_buffer && urb) {
+
+ urb->function_privdata = os_data;
+ urb->buffer = os_buffer;
+ urb->flags |= npd->recv_urb_flags;
+ urb->alloc_length = urb->buffer_length = alloc_length;
+ if (usbd_start_out_urb(urb)) {
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ urb->function_privdata = NULL;
+ urb->buffer = NULL;
+ usbd_free_urb(urb);
+ }
+ otg_atomic_inc(recv_urbs_started);
+ continue;
+ }
+ TRACE_MSG1(NTT, "recv_urbs_started: %d FAILED EARLY", otg_atomic_read(recv_urbs_started));
+
+ if (os_buffer || os_data)
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ if (urb)
+ urb->buffer = NULL;
+ usbd_free_urb(urb);
+ break;
+ }
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(NTT, "recv_urbs_started: %d", otg_atomic_read(recv_urbs_started));
+ return 0;
+}
+
+#if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+/*! blan_start_recv - start recv urb(s)
+ *
+ *@param function_instance - pointer to this function instance
+ *@return none
+ */
+int blan_start_recv(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ TRACE_MSG0(NTT, "DIRECT");
+ blan_start_recv_endpoint(function_instance, BULK_OUT_A, &npd->recv_urbs_started[0], "BULK_OUT_A");
+ #ifdef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ blan_start_recv_endpoint(function_instance, BULK_OUT_B, &npd->recv_urbs_started[1], "BULK_OUT_B");
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ return 0;
+}
+#else /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+
+/*! blan_start_recv_task - start recv urb(s)
+ *
+ * For high speed devices we can optimize using FAST_RECV flag to get urb handed
+ * directly to recv urb function from ISR. But then we do not want to allocate new
+ * recv urbs, so must implement a task for that.
+ *
+ *@param data - pointer to this function instance
+ *@return none
+ */
+void * blan_start_recv_task(otg_task_arg_t data)
+{
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *)data;
+ struct usb_network_private *npd = function_instance->privdata;
+ TRACE_MSG0(NTT, "--");
+ blan_start_recv_endpoint(function_instance, BULK_OUT_A, &npd->recv_urbs_started[0], "BULK_OUT_A");
+ #ifdef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ blan_start_recv_endpoint(function_instance, BULK_OUT_B, &npd->recv_urbs_started[1], "BULK_OUT_B");
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ return NULL;
+}
+/*! blan_start_recv - start recv urb(s)
+ *
+ *@param function_instance - pointer to this function instance
+ *@return none
+ */
+int blan_start_recv(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ TRACE_MSG0(NTT, "SCHEDULE");
+ otg_up_work(npd->blan_recv_task);
+ return 0;
+}
+#endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+
+/*! net_fd_start_recv_mdlm - start recv urb(s)
+ *
+ * For high speed devices we can optimize using FAST_RECV flag to get urb handed
+ * directly to recv urb function from ISR. But then we do not want to allocate new
+ * recv urbs, so must implement a task for that.
+ *
+ *@param data - pointer to this function instance
+ *@return none
+ */
+int net_fd_start_recv_mdlm (struct usbd_function_instance *function_instance)
+{
+ blan_start_recv(function_instance);
+ return 0;
+}
+
+/* ********************************************************************************************* */
+
+
+/*! net_fd_function_enable
+ * @brief enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance - pointer ot instance of this function
+ * @return int indicating function driver enable operation result
+ */
+
+int blan_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ struct usbd_class_network_channel_descriptor *channel = &blan_class_5 ;
+ struct usb_network_private *npd = NULL;
+ u32 recv_urb_flags;
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ struct usbd_class_ethernet_networking_descriptor *ethernet = &blan_class_4;
+ char address_str[14];
+ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#else
+ char address_str[20];
+ sprintf(address_str, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#endif
+ ethernet->iMACAddress = usbd_alloc_string(address_str);
+#endif
+ struct usbd_class_ethernet_networking_descriptor *ethernet = &blan_class_4;
+ char address_str[20];
+
+ sprintf(address_str, "%02x%02x%02x%02x%02x%02x", 0, 0, 0, 0, 0, 0);
+ ethernet->iMACAddress = usbd_alloc_string(function_instance, address_str);
+
+ //channel->iName = usbd_alloc_string(function_instance, system_utsname.nodename);
+
+ //TRACE_MSG3(NTT, "name: %s strings index imac: %d name: %d",
+ // system_utsname.nodename, ethernet->iMACAddress, channel->iName);
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ recv_urb_flags = 0;
+ #else /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+ recv_urb_flags = USBD_URB_FAST_RETURN | USBD_URB_FAST_FINISH;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+
+
+ THROW_IF(net_fd_function_enable(function_instance,
+ network_blan, net_fd_recv_urb_mdlm,
+ net_fd_start_xmit_mdlm,
+ blan_start_recv, recv_urb_flags
+ ), error);
+
+ THROW_UNLESS((npd = function_instance->privdata), error);
+
+ #if !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ THROW_UNLESS((npd->blan_recv_task = otg_task_init2("blanrcv", blan_start_recv_task, function_instance, NTT)), error);
+ //npd->blan_recv_task->taskdebug = TRUE;
+ otg_task_start(npd->blan_recv_task);
+ #endif /* !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+
+ return 0;
+
+ CATCH(error) {
+ return -EINVAL;
+ }
+}
+
+/*! blan_fd_function_disable - disable the function driver
+ *
+ * @param function_instance - pointer to function instance
+ *
+ * @return none
+ *
+ */
+void blan_fd_function_disable (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ #if !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ if (npd->blan_recv_task) {
+ otg_task_exit(npd->blan_recv_task);
+ npd->blan_recv_task = NULL;
+ }
+ #endif /* !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+ net_fd_function_disable(function_instance);
+}
+
+
+/*!
+ * blan_fd_set_configuration - called to indicate set configuration request was received
+ * @param function_instance
+ * @param configuration
+ * @return int
+ */
+int blan_fd_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int hs = usbd_high_speed(function_instance);
+
+ //struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+
+ TRACE_MSG2(NTT, "CONFIGURED: %d ip_addr: %08x", configuration, npd->ip_addr);
+
+ //net_check_mesg(mesg_configured);
+ if (npd->eem_os_buffer)
+ net_os_dealloc_buffer(function_instance, npd->eem_os_data, npd->eem_os_buffer);
+
+ npd->max_recv_urbs = hs ? NETWORK_START_URBS * 3 : NETWORK_START_URBS;
+ otg_atomic_clr(&npd->recv_urbs_started[0]);
+ #ifdef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ otg_atomic_clr(&npd->recv_urbs_started[1]);
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ npd->eem_os_data = npd->eem_os_buffer = NULL;
+ //npd->flags |= NETWORK_CONFIGURED;
+ npd->altsetting = 0;
+
+ if ((npd->flags & NETWORK_OPEN))
+ net_os_send_notification_later(function_instance);
+
+ TRACE_MSG1(NTT, "START RECV npd->flags: %04x", npd->flags);
+
+ net_fd_start(function_instance);
+
+ TRACE_MSG1(NTT, "CONFIGURED npd->flags: %04x", npd->flags);
+ return 0;
+}
+
+/*!
+ * @brief blan_fd_reset - called to indicate bus has been reset
+ * @param function_instance
+ * @return int
+ */
+int blan_fd_reset (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ TRACE_MSG1(NTT,"RESET %08x",npd->ip_addr);
+ return net_fd_stop (function_instance);
+}
+
+
+/*! blan_fd_urb_received_ep0 - callback for sent URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @return non-zero for failure.
+ */
+int blan_fd_urb_received_ep0 (struct usbd_urb *urb, int urb_rc)
+{
+ TRACE_MSG2(NTT,"urb: %p status: %d", urb, urb->status);
+
+ RETURN_EINVAL_IF (USBD_URB_OK != urb->status);
+
+ // TRACE_MSG1(NTT,"%s", urb->buffer); // QQSV is this really a NUL-terminated string???
+
+ return -EINVAL; // caller will de-allocate
+}
+/*! blan_fd_device_request
+ * @brief process a received SETUP URB
+ *
+ * Processes a received setup packet and CONTROL WRITE data.
+ * Results for a CONTROL READ are placed in urb->buffer.
+ *
+ * @param function_instance - pointer to this function instance
+ * @param request - received request to interface or endpoint
+ * @return non-zero for failure.
+ */
+int blan_fd_device_request (struct usbd_function_instance *function_instance, struct usbd_device_request *request)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb;
+ int index;
+
+ /* Verify that this is a USB Class request per CDC specification or a vendor request.
+ */
+ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
+
+ /* Determine the request direction and process accordingly
+ */
+ switch (request->bmRequestType & (USB_REQ_DIRECTION_MASK | USB_REQ_TYPE_MASK)) {
+
+ case USB_REQ_HOST2DEVICE | USB_REQ_TYPE_VENDOR:
+
+ switch (request->bRequest) {
+ case MCCI_ENABLE_CRC:
+ //if (make_crc_table())
+ // return -EINVAL;
+ //npd->encapsulation = simple_crc;
+ return 0;
+
+ case BELCARRA_PING:
+ TRACE_MSG1(NTT,"H2D VENDOR IP: %08x", npd->ip_addr);
+ if ((npd->network_type == network_blan))
+ net_os_send_notification_later(function_instance);
+ break;
+
+ #if !defined(CONFIG_OTG_NETWORK_BLAN_DO_NOT_SETTIME) || !defined(CONFIG_OTG_NETWORK_SAFE_DO_NOT_SETTIME)
+ case BELCARRA_SETTIME:
+ npd->rfc868time = ntohl( request->wValue << 16 | request->wIndex);
+ net_os_settime(function_instance, npd->rfc868time);
+ break;
+ #endif
+ case BELCARRA_SETIP:
+ #ifdef OTG_BIG_ENDIAN
+ npd->ip_addr = ntohl( request->wValue | request->wIndex<< 16 );
+ #else /* OTG_BIG_ENDIAN */
+ npd->ip_addr = ntohl( request->wValue << 16 | request->wIndex);
+ #endif /* OTG_BIG_ENDIAN */
+ TRACE_MSG1(NTT, "npd->ip_addr: %08x", npd->ip_addr);
+ // XXX need to get in correct order here
+ // XXX what about locally set mac addr
+ // XXX UNLESS(npd->local_dev_set) {
+ if (!(npd->override_MAC)) {
+ npd->local_dev_addr[0] = 0xfe | 0x02; /* locally administered */
+ npd->local_dev_addr[1] = 0x00;
+ npd->local_dev_addr[2] = (npd->ip_addr >> 24) & 0xff;
+ npd->local_dev_addr[3] = (npd->ip_addr >> 16) & 0xff;
+ npd->local_dev_addr[4] = (npd->ip_addr >> 8) & 0xff;
+ npd->local_dev_addr[5] = (npd->ip_addr >> 0) & 0xff;
+ }
+ // XXX }
+ break;
+
+ case BELCARRA_SETMSK:
+ #ifdef OTG_BIG_ENDIAN
+ npd->network_mask = ntohl( request->wValue | request->wIndex << 16);
+ #else /* OTG_BIG_ENDIAN */
+ npd->network_mask = ntohl( request->wValue << 16 | request->wIndex);
+ #endif /* OTG_BIG_ENDIAN */
+ TRACE_MSG1(NTT, "npd->network_mask: %08x", npd->network_mask);
+ break;
+
+ case BELCARRA_SETROUTER:
+ #ifdef OTG_BIG_ENDIAN
+ npd->router_ip = ntohl( request->wValue | request->wIndex << 16);
+ #else /* OTG_BIG_ENDIAN */
+ npd->router_ip = ntohl( request->wValue << 16 | request->wIndex);
+ #endif /* OTG_BIG_ENDIAN */
+ TRACE_MSG1(NTT, "npd->router_ip: %08x", npd->router_ip);
+ break;
+
+ case BELCARRA_SETDNS:
+ #ifdef OTG_BIG_ENDIAN
+ npd->dns_server_ip = ntohl( request->wValue | request->wIndex < 16);
+ #else /* OTG_BIG_ENDIAN */
+ npd->dns_server_ip = ntohl( request->wValue << 16 | request->wIndex);
+ #endif /* OTG_BIG_ENDIAN */
+ break;
+ #ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
+ case BELCARRA_SETFERMAT:
+ npd->fermat = 1;
+ break;
+ #endif
+ #ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME
+ case BELCARRA_HOSTNAME:
+ TRACE_MSG0(NTT,"HOSTNAME");
+ RETURN_EINVAL_IF(!(urb = usbd_alloc_urb_ep0(function_instance, le16_to_cpu(request->wLength),
+ blant_fd_urb_received_ep0) ));
+
+ RETURN_ZERO_IF(!usbd_start_out_urb(urb)); // return if no error
+ usbd_free_urb(urb); // de-alloc if error
+ return -EINVAL;
+ #endif
+ #ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK
+ case BELCARRA_DATA_NOTIFY:
+ TRACE_MSG0(NTT,"DATA NOTIFY");
+ npd->data_notify = 1;
+ return -EINVAL;
+ #endif
+ }
+ switch (request->bRequest) {
+ case BELCARRA_SETIP:
+ case BELCARRA_SETMSK:
+ case BELCARRA_SETROUTER:
+ TRACE_MSG5(NTT, "npd->network_mask: %08x npd->ip_addr: %08x npd->router_ip: %08x flags: %08x %s",
+ npd->network_mask, npd->ip_addr, npd->router_ip, npd->flags,
+ (npd->flags & NETWORK_CONFIGURED) ? "CONFIGURED" : "");
+
+ BREAK_UNLESS (npd->network_mask && npd->ip_addr && npd->router_ip && (npd->flags & NETWORK_CONFIGURED));
+ // let the os layer know, if it's interested.
+ #ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG
+ net_os_config(function_instance);
+ net_os_hotplug(function_instance);
+ #endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */
+ break;
+ }
+ return 0;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+/* ********************************************************************************************* */
+/*! blan_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations blan_fd_function_ops = {
+ .function_enable = blan_fd_function_enable,
+ .function_disable = blan_fd_function_disable,
+
+ .device_request = blan_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .set_configuration = blan_fd_set_configuration,
+ .set_interface = NULL,
+ .reset = blan_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+/*! function driver description
+ */
+struct usbd_interface_driver blan_interface_driver = {
+ .driver = {
+ .name = "net-blan-if",
+ .fops = &blan_fd_function_ops, },
+ .interfaces = sizeof (blan_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = blan_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = net_fd_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ #if !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+ .iFunction = "Belcarra USBLAN - MDLM/BLAN",
+ #elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+ .iFunction = "Belcarra USBLAN - MDLM/BLAN (DIN",
+ #elif !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+ .iFunction = "Belcarra USBLAN - MDLM/BLAN (DOUT)",
+ #elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+ .iFunction = "Belcarra USBLAN - MDLM/BLAN (DIN/DOUT)",
+ #endif /* defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT) */
+};
+
+/* ********************************************************************************************* */
+
+/*! int blan_mod_init()
+ * @brief - set interface bmDataCapabilities, and register driver to initialize module
+ * @return int for driver register result
+ */
+int blan_mod_init (void)
+{
+ struct usbd_class_ethernet_networking_descriptor *ethernet;
+ struct usbd_class_network_channel_descriptor *channel;
+
+ int len = 0;
+ char buf[255];
+
+ buf[0] = 0;
+
+#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
+ TRACE_MSG0(NTT,"fermat");
+ fermat_init();
+#endif
+
+ //blan_alternate_descriptions[0].endpoints = Usb_network_private.have_interrupt ? 3 : 2;
+
+#if 0
+ // Update the iMACAddress field in the ethernet descriptor
+ {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ char address_str[14];
+ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#else
+ char address_str[20];
+ sprintf(address_str, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#endif
+ TRACE_MSG0(NTT,"alloc mac string");
+ //if ((ethernet = &blan_class_4))
+ // ethernet->iMACAddress = usbd_alloc_string(function_instance, address_str);
+ TRACE_MSG0(NTT,"alloc mac string done");
+ }
+ TRACE_MSG0(NTT,"alloc channel string");
+ //if ((channel = &blan_class_5))
+ // channel->iName = usbd_alloc_string(function_instance, system_utsname.nodename);
+ TRACE_MSG0(NTT,"alloc channel string done");
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_PADBYTES
+ blan_class_3.bPad = CONFIG_OTG_NETWORK_BLAN_PADBYTES;
+ len += sprintf(buf + len, "PADBYTES: %02x ", blan_class_3.bPad);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_PADBEFORE
+ blan_class_3.bmDataCapabilities |= BMDATA_PADBEFORE;
+ len += sprintf(buf + len, "PADBEFORE: %02x ", blan_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_PADAFTER
+ blan_class_3.bmDataCapabilities |= BMDATA_PADAFTER;
+ len += sprintf(buf + len, "PADAFTER: %02x ", blan_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_CRC
+ blan_class_3.bmDataCapabilities |= BMDATA_CRC;
+ len += sprintf(buf + len, "CRC: %02x ", blan_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
+ blan_class_3.bmDataCapabilities |= BMDATA_FERMAT;
+ len += sprintf(buf + len, "FERMAT: %02x ", blan_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_HOSTNAME
+ blan_class_3.bmDataCapabilities |= BMDATA_HOSTNAME;
+ len += sprintf(buf + len, "HOSTNAME: %02x ", blan_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_NONBRIDGED
+ #ifdef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG
+ #warning BLAN_AUTO_CONFIG ignored when BLAN_NONBRIDGED enabled
+ #endif /* CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG */
+ blan_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED | BMNETWORK_NO_VENDOR;
+ len += sprintf(buf + len, "NONBRIDGE: %02x ", blan_class_3.bmNetworkCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_BLAN_DATA_NOTIFY_OK
+ blan_class_3.bmNetworkCapabilities |= BMNETWORK_DATA_NOTIFY_OK;
+ len += sprintf(buf + len, "DATA NOTIFY: %02x ", blan_class_3.bmNetworkCapabilities);
+#endif
+#ifndef CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG
+ blan_class_3.bmNetworkCapabilities |= BMNETWORK_NO_VENDOR;
+ len += sprintf(buf + len, "NO VENDOR: %02x ", blan_class_3.bmNetworkCapabilities);
+#endif
+ if (strlen(buf))
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf);
+
+ return usbd_register_interface_function (&blan_interface_driver, "net-blan-if", NULL);
+}
+
+/*!
+ * @brief blan_mod_exit
+ * @return none
+ */
+void blan_mod_exit(void)
+{
+ usbd_deregister_interface_function (&blan_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_BLAN */
+/*!
+ * @brief blan_mod_init
+ * @return int
+ */
+int blan_mod_init (void)
+{
+ return 0;
+}
+/*!
+ * @brief blan_mod_exit
+ * @return none
+ */
+void blan_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_BLAN */
diff --git a/drivers/otg/functions/network/ecm-if.c b/drivers/otg/functions/network/ecm-if.c
new file mode 100644
index 000000000000..d7ce6a3ee61d
--- /dev/null
+++ b/drivers/otg/functions/network/ecm-if.c
@@ -0,0 +1,652 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/ecm.c - Network Function Driver
+ * @(#) balden@belcarra.com|otg/functions/network/ecm-if.c|20070326205353|49276
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/ecm-if.c
+ * @brief This file implements the required descriptors to implement
+ * a basic network device with two interfaces.
+ *
+ * The CDC ECM network driver implements the CDC Network Configuration. Two
+ * interfaces with two BULK data endpoints and an INTERRUPT endpoint.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+#include "net-os.h"
+
+#define NTT network_fd_trace_tag
+
+#if defined(CONFIG_OTG_NETWORK_ECM) || defined(_OTG_DOXYGEN)
+
+/* USB CDC Configuration ******************************************************************** */
+
+/*! @name CDC ECM descriptors group
+ *
+ * @{
+ */
+static struct usbd_class_header_function_descriptor ecm_class_1 = {
+ bFunctionLength: 0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_HEADER,
+ bcdCDC: __constant_cpu_to_le16(0x0110),
+};
+
+
+static struct usbd_class_ethernet_networking_descriptor ecm_class_2 = {
+ bFunctionLength: 0x0D,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_ENF,
+ iMACAddress: 0x00,
+ bmEthernetStatistics: 0x00,
+ wMaxSegmentSize: __constant_cpu_to_le16(0x05ea),
+ wNumberMCFilters: 0,
+ bNumberPowerFilters: 0,
+};
+
+static struct usbd_class_union_function_descriptor ecm_class_3 =
+{
+ bFunctionLength: 0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_UF,
+ bMasterInterface: 0x00,
+ bSlaveInterface: { 1 }, // This will be updated
+
+};
+
+static usbd_class_descriptor_t *ecm_comm_class_descriptors[] = {
+ (struct usbd_generic_class_descriptor *) &ecm_class_1,
+ (struct usbd_generic_class_descriptor *) &ecm_class_2,
+ (struct usbd_generic_class_descriptor *) &ecm_class_3,
+};
+
+/*!
+ * @}
+ */
+
+/*!@name Data Alternate Interface Description List
+ *
+ * @{
+ */
+static struct usbd_alternate_description ecm_comm_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - CDC/ECM",
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bInterfaceProtocol = 0,
+ .classes = sizeof (ecm_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = ecm_comm_class_descriptors,
+ .endpoints = 1,
+ .endpoint_index = cdc_int_endpoint_index,
+ },
+};
+
+static struct usbd_alternate_description ecm_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - CDC/ECM No Data",
+ .bInterfaceClass = USB_CLASS_DATA, // XXX
+ .bInterfaceSubClass = 0, // XXX
+ .bInterfaceProtocol = 0, // XXX
+ },
+ {
+ .iInterface = "Belcarra USBLAN - CDC/ECM Data",
+ .bInterfaceClass = USB_CLASS_DATA, // XXX
+ .bInterfaceSubClass = 0x0, // XXX
+ .bInterfaceProtocol = 0x0, // XXX
+ .endpoints = 2,
+ .endpoint_index = cdc_data_endpoint_index,
+ },
+};
+
+/*!
+ * @}
+ */
+
+
+/* CDC Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+struct usbd_interface_description ecm_interfaces[] = {
+ {
+ .alternates = sizeof (ecm_comm_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = ecm_comm_alternate_descriptions,
+ },
+ {
+ .alternates = sizeof (ecm_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = ecm_data_alternate_descriptions,
+ },
+};
+
+/* ********************************************************************************************* */
+/*! net_fd_start_xmit_nocrc
+ * @brief - start sending a buffer
+ *
+ * @param function_instance - pointer to this function instance
+ * @param buffer buffer containing data to send
+ * @param len - length of data to send
+ * @param data instance data
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_xmit_nocrc (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+ int in_pkt_sz;
+ u8 *cp;
+ u32 crc;
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ int xmit_index = 0;
+ int endpoint_index = BULK_IN_A;
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ int xmit_index = (otg_atomic_read(&npd->xmit_urbs_started[0]) <= otg_atomic_read(&npd->xmit_urbs_started[1]))
+ ? 0 : 1;
+ int endpoint_index = (xmit_index) ? BULK_IN_B : BULK_IN_A;
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+
+ TRACE_MSG7(NTT,"npd: %p function: %p flags: %04x len: %d endpoint_index: %d xmit_started: %d %d",
+ npd, function_instance, npd->flags, len, endpoint_index,
+ otg_atomic_read(&npd->xmit_urbs_started[0]), otg_atomic_read(&npd->xmit_urbs_started[1]));
+
+ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+
+ /* allocate urb 5 bytes larger than required */
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, len + 5 + in_pkt_sz, net_fd_urb_sent_bulk ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa);
+ return -ENOMEM;
+ }
+
+ urb->actual_length = len;
+
+ memcpy (urb->buffer, buffer, len);
+ urb->function_privdata = data;
+ urb->actual_length = len;
+ otg_atomic_add(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_inc(&npd->xmit_urbs_started[xmit_index]);
+ if ((rc = usbd_start_in_urb (urb))) {
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+ urb->function_privdata = NULL;
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_dec(&npd->xmit_urbs_started[xmit_index]);
+ usbd_free_urb (urb);
+ return rc;
+ }
+ return 0;
+}
+
+/*! net_fd_start_xmit_ecm
+ * @brief - start sending to host
+ * @param function_instance
+ * @param buffer data storage area
+ * @param len - length of data to transmit
+ * @param data - instance private data
+ *
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_xmit_ecm (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+ int in_pkt_sz;
+ u32 crc;
+ //#ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ int xmit_index = 0;
+ int endpoint_index = BULK_IN_A;
+ //#else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ //int xmit_index = (otg_atomic_read(&npd->xmit_urbs_started[0]) <= otg_atomic_read(&npd->xmit_urbs_started[1]))
+ // ? 0 : 1;
+ //int endpoint_index = (xmit_index) ? BULK_IN_B : BULK_IN_A;
+ //#endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+
+ TRACE_MSG7(NTT,"npd: %p function: %p flags: %04x len: %d endpoint_index: %d xmit_started: %d %d",
+ npd, function_instance, npd->flags, len, endpoint_index,
+ otg_atomic_read(&npd->xmit_urbs_started[0]), otg_atomic_read(&npd->xmit_urbs_started[1]));
+
+ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+
+ //TRACE_MSG0(NTT,"SIMPLE_CRC");
+ // allocate urb 5 bytes larger than required
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_bulk ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa);
+ return -ENOMEM;
+ }
+ urb->actual_length = len;
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+
+ // copy and crc len bytes
+ crc = crc32_copy(urb->buffer, buffer, len, CRC32_INIT);
+
+ if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz - 4)) {
+
+ #undef CONFIG_OTG_NETWORK_PADBYTE
+ #ifdef CONFIG_OTG_NETWORK_PADBYTE
+ // add a pad byte if required to ensure a short packet, usbdnet driver
+ // will correctly handle pad byte before or after CRC, but the MCCI driver
+ // wants it before the CRC.
+ crc = crc32_pad(urb->buffer + urb->actual_length, 1, crc);
+ urb->actual_length++;
+ #else /* CONFIG_OTG_NETWORK_PADBYTE */
+ urb->flags |= USBD_URB_SENDZLP;
+ TRACE_MSG2(NTT,"setting ZLP: urb: %p flags: %x", urb, urb->flags);
+ #endif /* CONFIG_OTG_NETWORK_PADBYTE */
+ }
+ crc = ~crc;
+ urb->buffer[urb->actual_length++] = crc & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 8) & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 16) & 0xff;
+ urb->buffer[urb->actual_length++] = (crc >> 24) & 0xff;
+ #if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT)
+ if (npd->fermat) fermat_encode(urb->buffer, urb->actual_length);
+ #endif
+
+ #else /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+ memcpy (urb->buffer, buffer, len);
+ if ((urb->actual_length % in_pkt_sz) == (in_pkt_sz)) {
+ /* no longer in Kconfig - change undef to define to active padbyte */
+ #undef CONFIG_OTG_NETWORK_PADBYTE
+ #ifdef CONFIG_OTG_NETWORK_PADBYTE
+ urb->actual_length++;
+ #else /* CONFIG_OTG_NETWORK_PADBYTE */
+ urb->flags |= USBD_URB_SENDZLP;
+ TRACE_MSG2(NTT,"setting ZLP: urb: %p flags: %x", urb, urb->flags);
+ #endif /* CONFIG_OTG_NETWORK_PADBYTE */
+ }
+
+
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+ //TRACE_MSG3(NTT,"urb: %p buf: %p priv: %p", urb, data, urb->function_privdata);
+ urb->function_privdata = data;
+
+ otg_atomic_add(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_inc(&npd->xmit_urbs_started[xmit_index]);
+ if ((rc = usbd_start_in_urb (urb))) {
+
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+ urb->function_privdata = NULL;
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_dec(&npd->xmit_urbs_started[xmit_index]);
+ usbd_free_urb (urb);
+
+ return rc;
+ }
+ return 0;
+}
+
+/*! net_fd_recv_urb_ecm
+ * @brief - callback to process a received URB
+ *
+ * @param urb - received urb
+ * @param rc dummy
+ * @return non-zero for failure.
+ */
+int net_fd_recv_urb_ecm(struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ void *os_data = NULL;
+ u8 *os_buffer, *net_frame;
+ int crc_bad = 0;
+ int trim = 0;
+ int len;
+ u32 crc;
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ int endpoint_index = BULK_OUT_A;
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ int endpoint_index = BULK_OUT_A;
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ int out_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ len = urb->actual_length;
+ trim = 0;
+
+ TRACE_MSG2(NTT, "status: %d actual_length: %d", urb->status, urb->actual_length);
+ //RETURN_EINVAL_IF (urb->status == USBD_URB_OK);
+
+ THROW_UNLESS((os_data = net_os_alloc_buffer(function_instance, &os_buffer, len)), error);
+ net_frame = os_buffer;
+
+ //TRACE_MSG2(NTT, "os_data: %x os_buffer: %x", os_data, os_buffer);
+
+
+ /*
+ * The CRC can be sent in two ways when the size of the transfer
+ * ends up being a multiple of the packetsize:
+ *
+ * |
+ * <data> <CRC><CRC><CRC><CRC>|<???> case 1
+ * <data> <NUL><CRC><CRC><CRC>|<CRC> case 2
+ * <data> <NUL><CRC><CRC><CRC><CRC>| case 3
+ * <data> <NUL><CRC><CRC><CRC>|<CRC> | case 4
+ * |
+ *
+ * This complicates CRC checking, there are four scenarios:
+ *
+ * 1. length is 1 more than multiple of packetsize with a trailing byte
+ * 2. length is 1 more than multiple of packetsize
+ * 3. length is multiple of packetsize
+ * 4. none of the above
+ *
+ * Finally, even though we always compute CRC, we do not actually throw
+ * things away until and unless we have previously seen a good CRC.
+ * This allows backwards compatibility with hosts that do not support
+ * adding a CRC to the frame.
+ *
+ */
+
+ // test if 1 more than packetsize multiple
+ if (1 == (len % out_pkt_sz)) {
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ // copy and CRC up to the packetsize boundary
+ crc = crc32_copy(os_buffer, urb->buffer, len - 1, CRC32_INIT);
+ os_buffer += len - 1;
+
+ // if the CRC is good then this is case 1
+ if (CRC32_GOOD != crc) {
+
+ crc = crc32_copy(os_buffer, urb->buffer + len - 1, 1, crc);
+ os_buffer += 1;
+
+ if (CRC32_GOOD != crc) {
+ //crc_errors[len%64]++;
+ TRACE_MSG2(NTT,"A CRC error %08x %03x", crc, urb->framenum);
+ THROW_IF(npd->seen_crc, crc_error);
+ }
+ else
+ npd->seen_crc = 1;
+ }
+ else
+ npd->seen_crc = 1;
+
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+ }
+ else {
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ crc = crc32_copy(os_buffer, urb->buffer, len, CRC32_INIT);
+ os_buffer += len;
+
+ if (CRC32_GOOD != crc) {
+ //crc_errors[len%64]++;
+ TRACE_MSG2(NTT,"B CRC error %08x %03x", crc, urb->framenum);
+ THROW_IF(npd->seen_crc, crc_error);
+ }
+ else
+ npd->seen_crc = 1;
+ #else /* !defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+ memcpy (os_buffer, urb->buffer, len);
+ #endif /* !defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+ }
+ // trim IFF we are paying attention to crc
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ if (npd->seen_crc)
+ trim = 4;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) ...*/
+
+
+ if (net_fd_recv_buffer(function_instance, net_frame, len, os_data, crc_bad, trim)) {
+ TRACE_MSG0(NTT, "FAILED");
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ }
+
+ // catch a simple error, just increment missed error and general error
+ CATCH(error) {
+ //TRACE_MSG4(NTT,"CATCH(error) urb: %p status: %d len: %d function: %p",
+ // urb, urb->status, urb->actual_length, function_instance);
+ // catch a CRC error
+ CATCH(crc_error) {
+ crc_bad = 1;
+ }
+ }
+ return 0;
+ //return usbd_start_out_urb (urb);
+}
+
+
+
+/* ********************************************************************************************* */
+#ifdef CONFIG_OTG_NETWORK_START_SINGLE
+#define NETWORK_START_URBS 1
+#else
+#define NETWORK_START_URBS 2
+#endif
+
+/*! net_fd_start_recv_ecm - start recv urb(s)
+ *
+ * @param function_instance - pointer to this function instance
+ * @return none
+ */
+int net_fd_start_recv_ecm(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int i;
+ int network_start_urbs = NETWORK_START_URBS;
+ int hs = usbd_high_speed(function_instance);
+ int endpoint_index = BULK_OUT_A;
+
+ if (hs)
+ network_start_urbs = NETWORK_START_URBS * 3;
+
+
+ for (i = 0; i < network_start_urbs; i++) {
+ struct usbd_urb *urb;
+ BREAK_IF(!(urb = usbd_alloc_urb(function_instance, endpoint_index,
+ usbd_endpoint_transferSize(function_instance, endpoint_index, hs),
+ net_fd_recv_urb)));
+ TRACE_MSG5(NTT,"i: %d urb: %p priv: %p npd: %p function: %p",
+ i, urb, urb->function_privdata, npd, function_instance);
+
+ //urb->function_privdata = npd;
+ if (usbd_start_out_urb(urb)) {
+ urb->function_privdata = NULL;
+ usbd_free_urb(urb);
+ }
+ }
+ return 0;
+}
+/* ********************************************************************************************* */
+
+/*! ecm_fd_function_enable
+ * @brief - enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance - function instance pointer
+ * @return int - 0 for success
+ */
+
+int ecm_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd;
+ struct usbd_class_ethernet_networking_descriptor *ethernet = &ecm_class_2;
+
+ char address_str[14];
+
+ /* do normal network function enable first - this creates network private data */
+ RETURN_EINVAL_IF(net_fd_function_enable(function_instance, network_ecm,
+ net_fd_recv_urb_ecm,
+ net_fd_start_xmit_ecm,
+ net_fd_start_recv_ecm, 0
+ ));
+
+ npd = function_instance->privdata;
+
+ /* if successful then lets allocate iMACAddress string */
+
+ TRACE_MSG6(NTT, "local: %02x:%02x:%02x:%02x:%02x:%02x",
+ npd->local_dev_addr[0], npd->local_dev_addr[1], npd->local_dev_addr[2],
+ npd->local_dev_addr[3], npd->local_dev_addr[4], npd->local_dev_addr[5]);
+
+ TRACE_MSG6(NTT, "remote: %02x:%02x:%02x:%02x:%02x:%02x",
+ npd->remote_dev_addr[0], npd->remote_dev_addr[1], npd->remote_dev_addr[2],
+ npd->remote_dev_addr[3], npd->remote_dev_addr[4], npd->remote_dev_addr[5]);
+
+ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+ npd->remote_dev_addr[0], npd->remote_dev_addr[1], npd->remote_dev_addr[2],
+ npd->remote_dev_addr[3], npd->remote_dev_addr[4], npd->remote_dev_addr[5]);
+
+ ethernet->iMACAddress = usbd_alloc_string(function_instance, address_str);
+
+ return 0;
+}
+
+
+/*! ecm_fd_set_configuration
+ * @brief - called to indicate set configuration request was received
+ * @param function_instance
+ * @param configuration
+ * @return int
+ */
+int ecm_fd_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ //struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+ //net_check_mesg(mesg_configured);
+ if (npd->eem_os_buffer)
+ net_os_dealloc_buffer(function_instance, npd->eem_os_data, npd->eem_os_buffer);
+
+ npd->eem_os_data = npd->eem_os_buffer = NULL;
+ //npd->flags |= NETWORK_CONFIGURED;
+ npd->altsetting = 0;
+ TRACE_MSG3(NTT, "CONFIGURED: %d ipaddr: %08x flags: %04x", configuration, npd->ip_addr, npd->flags);
+ return 0;
+}
+
+
+
+/*!ecm_fd_set_interface
+ *
+ * @brief called to indicate set interface request was received
+ * @param function_instance
+ * @param wIndex
+ * @param altsetting
+ * @return int
+ */
+int ecm_fd_set_interface (struct usbd_function_instance *function_instance, int wIndex, int altsetting)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ TRACE_MSG2(NTT, "SET INTERFACE[%02x] altsetting: %02x", wIndex, altsetting);
+ npd->altsetting = altsetting;
+ TRACE_MSG3(NTT, "ipaddr: %08x flags: %04x altsetting: %02x",
+ npd->ip_addr, npd->flags, npd->altsetting);
+ return (altsetting ? net_fd_start : net_fd_stop) (function_instance);
+}
+
+
+
+/* ********************************************************************************************* */
+/*! ecm_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations ecm_fd_function_ops = {
+ .function_enable = ecm_fd_function_enable,
+ .function_disable = net_fd_function_disable,
+
+ .device_request = net_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .set_configuration = ecm_fd_set_configuration,
+ .set_interface = ecm_fd_set_interface,
+ .reset = net_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+
+/*! function driver description
+ */
+struct usbd_interface_driver ecm_interface_driver = {
+ .driver = {
+ .name = "cdc-ecm-if",
+ .fops = &ecm_fd_function_ops, },
+ .interfaces = sizeof (ecm_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = ecm_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = net_fd_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .iFunction = "Belcarra USBLAN - CDC/ECM",
+};
+
+/* ********************************************************************************************* */
+
+/*! emc_mod_init
+ * @brief initialize ecm interface module
+ * @return int
+ */
+int ecm_mod_init (void)
+{
+ return usbd_register_interface_function (&ecm_interface_driver, "cdc-ecm-if", NULL);
+}
+/*! ecm_mod_exit
+ * @brief cleanup system resource
+ * @return void
+ */
+
+void ecm_mod_exit(void)
+{
+ usbd_deregister_interface_function (&ecm_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_ECM */
+/*!
+ * @brief ecm_mod_init
+ * @return int
+ */
+int ecm_mod_init (void)
+{
+ return 0;
+}
+
+void ecm_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_ECM */
diff --git a/drivers/otg/functions/network/eem-if.c b/drivers/otg/functions/network/eem-if.c
new file mode 100644
index 000000000000..d0195043c7f1
--- /dev/null
+++ b/drivers/otg/functions/network/eem-if.c
@@ -0,0 +1,835 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/eem.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/eem-if.c|20070315225839|19712
+ *
+ * Copyright (c) 2005-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/eem-if.c
+ * @brief This file implements the required descriptors to implement
+ * a eem network device with a single interface.
+ *
+ * The EEM network driver implements a very simple descriptor set,
+ * a single interface with two BULK data endpoints.
+ *
+ * The EEM protocol implements a different data encapsulation than
+ * the other procotols (CDC ECM, MDLM-BLAN etc.) which can support
+ * data streaming.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+#include "net-os.h"
+
+#define NTT network_fd_trace_tag
+
+#if defined(CONFIG_OTG_NETWORK_EEM) || defined(_OTG_DOXYGEN)
+
+/* USB EEM Configuration ******************************************************************** */
+
+/*! Data Alternate Interface Description List
+ */
+static struct usbd_alternate_description eem_data_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra - CDC/EEM",
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = COMMUNICATIONS_EEM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_EEM_PROTOCOL,
+ .endpoints = 2,
+ .endpoint_index = cdc_data_endpoint_index,
+ },
+};
+
+
+/* EEM Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+struct usbd_interface_description eem_interfaces[] = {
+ {
+ .alternates = sizeof (eem_data_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = eem_data_alternate_descriptions,
+ },
+};
+
+
+/* ********************************************************************************************* */
+
+/*! net_fd_urb_sent_zle - callback for completed BULK ZLE URB
+ *
+ * Handles notification that an ZLE urb has been sent (successfully or otherwise).
+ *
+ * @param urb Pointer to the urb that has been sent.
+ * @param urb_rc Result code from the send operation.
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_urb_sent_zle (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ unsigned long flags;
+ void *buf;
+ int rc = -EINVAL;
+ int in_pkt_sz;
+
+ TRACE_MSG6(NTT,"urb: %p urb_rc: %d length: %d queue: %d avg: %d samples: %d",
+ urb, urb_rc, urb->actual_length, otg_atomic_read(&npd->queued_frames),
+ npd->avg_queue_frames / npd->samples, npd->samples);
+
+ usbd_free_urb (urb);
+ rc = 0;
+ return rc;
+}
+
+/*! net_fd_urb_sent_eem_bulk - callback for completed BULK xmit URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @param urb Pointer to the urb that has been sent.
+ * @param urb_rc Result code from the send operation.
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_urb_sent_eem_bulk (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ int rc;
+ int in_pkt_sz;
+ int endpoint_index = BULK_IN_A;
+
+ rc = net_fd_urb_sent_bulk(urb, urb_rc);
+
+ #ifdef CONFIG_OTG_NETWORK_EEM_STREAM
+ /* see if we should send ZLE */
+ if (otg_atomic_read(&npd->queued_frames) || (npd->network_type != network_eem))
+ return rc;
+
+ /* Nothing left to send so stop the host transfer by sending a ZLE */
+ UNLESS((usbd_get_device_status(function_instance) == USBD_OK))
+ return rc;
+
+ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ if ((urb = usbd_alloc_urb (function_instance, endpoint_index, in_pkt_sz, net_fd_urb_sent_zle ))) {
+ npd->eem_send_zle_data = 0;
+ #ifdef CONFIG_OTG_NETWORK_EEM_ZLE
+ urb->actual_length = 2 + (npd->eem_send_zle_data ? 1 : 0);
+ TRACE_MSG2(NTT, "Sending ZLE: length: %d %d", urb->actual_length, npd->eem_send_zle_data);
+ urb->buffer[0] = 0;
+ urb->buffer[1] = 0;
+ urb->buffer[3] = 0;
+ #else /* CONFIG_OTG_NETWORK_EEM_ZLE */
+ urb->actual_length = 0;
+ TRACE_MSG2(NTT, "Sending ZLP: length: %d %d", urb->actual_length, npd->eem_send_zle_data);
+ urb->flags |= USBD_URB_SENDZLP;
+ #endif /* CONFIG_OTG_NETWORK_EEM_ZLE */
+
+ if ((rc = usbd_start_in_urb (urb))) {
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ printk(KERN_ERR"%s: FAILED: %d\n", __FUNCTION__, rc);
+ urb->function_privdata = NULL;
+ usbd_free_urb (urb);
+ }
+ }
+ #endif /* CONFIG_OTG_NETWORK_EEM_STREAM */
+ return rc;
+}
+
+/*! net_fd_urb_sent_eem_echo - callback for completed BULK xmit URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @param urb Pointer to the urb that has been sent.
+ * @param urb_rc Result code from the send operation.
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_urb_sent_eem_echo (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ int in_pkt_sz;
+ unsigned long flags;
+
+ TRACE_MSG2(NTT, "urb: %x rc: %d", urb, urb_rc);
+ usbd_free_urb (urb);
+
+ return 0;
+}
+
+/*! net_fd_start_xmit_eem
+ * @brief start sending data, Called to send a network frame via EEM.
+
+ *
+ * @param function_instance - function instance pointer
+ * @param buffer - pointer to data to send
+ * @param len - length of buffer
+ * @param data - instance private data
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_xmit_eem (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+ int in_pkt_sz;
+ u8 *cp;
+ u32 crc = 0;
+ int length;
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ int xmit_index = 0;
+ int endpoint_index = BULK_IN_A;
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ int xmit_index = (otg_atomic_read(&npd->xmit_urbs_started[0]) <= otg_atomic_read(&npd->xmit_urbs_started[1]))
+ ? 0 : 1;
+ int endpoint_index = (xmit_index) ? BULK_IN_B : BULK_IN_A;
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+
+ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+
+ /* allocate urb 5 bytes larger than required */
+ length = len + 5 + 4 + in_pkt_sz;
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, length, net_fd_urb_sent_eem_bulk )))
+ {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa);
+ return -ENOMEM;
+ }
+ memset(urb->buffer, 0, length);
+ urb->function_privdata = data;
+
+ /* if EEM packet is multiple of packetsize we may need to append ZLE (two NULL bytes)
+ */
+ urb->actual_length = len + 2 + (npd->eem_send_zle_data ? 1 : 0) + 4;
+ cp = npd->eem_send_zle_data ? urb->buffer + 1 : urb->buffer;
+ npd->eem_send_zle_data = FALSE;
+
+ /* EEM packet header - set bmCRC if EEM CRC is enabled */
+ #if defined(CONFIG_OTG_NETWORK_EEM_CRC)
+ /* little endian */
+ cp[0] = (len + 4) & 0xff;
+ cp[1] = 0x40 | (((len + 4) >> 8) & 0x3f);
+ cp += 2;
+ crc = crc32_copy(cp, buffer, len, CRC32_INIT);
+ crc = ~crc;
+ /* append CRC - network byte order */
+ cp += len;
+ *cp++ = crc & 0xff;
+ *cp++ = (crc >> 8) & 0xff;
+ *cp++ = (crc >> 16) & 0xff;
+ *cp++ = (crc >> 24) & 0xff;
+ #else /* !defined(CONFIG_OTG_NETWORK_EEM_CRC) */
+ /* little endian */
+ cp[0] = (len + 4) & 0xff;
+ cp[1] = (((len + 4) >> 8) & 0x3f);
+ cp += 2;
+ memcpy (cp, buffer, len);
+ /* append 0xdeadbeef sentinel - network byte order */
+ cp += len;
+ *cp++ = 0xde;
+ *cp++ = 0xad;
+ *cp++ = 0xbe;
+ *cp++ = 0xef;
+ #endif /* !defined(CONFIG_OTG_NETWORK_EEM_CRC) */
+
+ #ifdef CONFIG_OTG_NETWORK_EEM_STREAM
+ /* if actual length is odd then our padding will have start of ZLE but without data
+ * set flag and pad with nulls to packetsize
+ */
+ npd->eem_send_zle_data = (urb->actual_length & 1);
+ while (urb->actual_length % in_pkt_sz)
+ urb->actual_length++;
+ #else /* CONFIG_OTG_NETWORK_EEM_STREAM */
+ /* send ZLP if neccessary */
+ UNLESS ((urb->actual_length % in_pkt_sz)) {
+ #ifdef CONFIG_OTG_NETWORK_EEM_ZLE
+ TRACE_MSG0(NTT,"ZLE");
+ urb->actual_length++;
+ urb->actual_length++;
+ #else /* CONFIG_OTG_NETWORK_EEM_ZLE */
+ TRACE_MSG0(NTT,"ZLP");
+ urb->flags |= USBD_URB_SENDZLP;
+ #endif /* CONFIG_OTG_NETWORK_EEM_ZLE */
+ }
+ #endif /* CONFIG_OTG_NETWORK_EEM_STREAM */
+
+ TRACE_MSG4(NTT, "len: %d crc: %08x actual_length: %d zle_data: %d", len, crc, urb->actual_length, npd->eem_send_zle_data);
+
+ otg_atomic_add(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_inc(&npd->xmit_urbs_started[xmit_index]);
+ if ((rc = usbd_start_in_urb (urb))) {
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ urb->function_privdata = NULL;
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_dec(&npd->xmit_urbs_started[xmit_index]);
+ usbd_free_urb (urb);
+ return rc;
+ }
+ return 0;
+}
+
+/*! net_fd_start_echo_eem
+ * @brief start sending data, called to send a network frame via EEM.
+ *
+ * @param function_instance - function instance pointer
+ * @param buffer - pointer to data to send
+ * @param len - length of data to send
+ * @param data - pointer to instance private data
+ * @param eem_command - eem command
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_echo_eem (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data, u8 eem_command)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+ int in_pkt_sz;
+ int xmit_index = 0;
+ int endpoint_index = BULK_IN_A;
+ u8 *cp;
+ u32 crc = 0;
+
+ in_pkt_sz = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index, usbd_high_speed(function_instance));
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+ /* allocate urb 5 bytes larger than required */
+ if (!(urb = usbd_alloc_urb (function_instance, endpoint_index, len + 5 + 4 + in_pkt_sz, net_fd_urb_sent_eem_echo ))) {
+ u8 epa = usbd_endpoint_bEndpointAddress(function_instance, endpoint_index, usbd_high_speed(function_instance));
+ TRACE_MSG2(NTT,"urb alloc failed len: %d endpoint: %02x", len, epa);
+ printk(KERN_ERR"%s: urb alloc failed len: %d endpoint: %02x\n", __FUNCTION__, len, epa);
+ return -ENOMEM;
+ }
+ urb->function_privdata = data;
+
+ /* if EEM packet is multiple of packetsize we may need to append ZLE (two NULL bytes)
+ */
+ urb->actual_length = len + 2 + (npd->eem_send_zle_data ? 1 : 0) + 4;
+ cp = npd->eem_send_zle_data ? urb->buffer + 1 : urb->buffer;
+ npd->eem_send_zle_data = FALSE;
+
+ /* EEM packet header - set bmCRC if EEM CRC is enabled, little endian */
+ cp[0] = (len + 4) & 0xff;
+ cp[1] = 0x80 | (eem_command << 11) | (((len + 4) >> 8) & 0x07);
+ cp += 2;
+ memcpy(cp, buffer, len);
+
+ TRACE_MSG4(NTT, "len: %d crc: %08x actual_length: %d zle_data: %d", len, crc, urb->actual_length, npd->eem_send_zle_data);
+
+ otg_atomic_add(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_inc(&npd->xmit_urbs_started[xmit_index]);
+ if ((rc = usbd_start_in_urb (urb))) {
+ TRACE_MSG1(NTT,"FAILED: %d", rc);
+ urb->function_privdata = NULL;
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ otg_atomic_dec(&npd->xmit_urbs_started[xmit_index]);
+ usbd_free_urb (urb);
+ return rc;
+ }
+ return 0;
+}
+
+
+/*! net_fd_eem_recv_data
+ *
+ * @brief Accumulate data from an EEM Data Packet, when finished pass to OS layer to
+ * push up to network layer.
+ *
+ * @param function_instance - function instance pointer
+ * @param eem_data_type
+ * @param frame_length - frame length
+ * @param urb_buffer -
+ * @param remainder
+ * @param shortTransfer
+ *
+ *
+ * @return Number of bytes consumed from urb buffer.
+ */
+STATIC int net_fd_eem_recv_data(struct usbd_function_instance *function_instance, eem_data_type_t eem_data_type,
+ int frame_length,
+ u8 *urb_buffer, int remainder, BOOL shortTransfer)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ void *os_data;
+ u8 *os_buffer;
+ int os_buffer_used, copy_length;
+ u32 eem_crc, valid_crc;
+ BOOL crc_bad;
+
+ // XXX return MIN(remainder, frame_length);
+
+ /* Restart filling if in the middle of receiving an EEM Data Packet.
+ */
+ if (npd->eem_os_buffer) {
+ //printk(KERN_INFO"%s: WWWW\n", __FUNCTION__);
+ os_data = npd->eem_os_data;
+ os_buffer = npd->eem_os_buffer;
+ os_buffer_used = npd->eem_os_buffer_used;
+ eem_data_type = npd->eem_data_type;
+ eem_crc = npd->eem_crc;
+ npd->eem_os_data = npd->eem_os_buffer = NULL;
+ frame_length = npd->eem_frame_length;
+ }
+ /* Otherwise allocate a new os buffer
+ */
+ else {
+ //printk(KERN_INFO"%s: XXXX\n", __FUNCTION__);
+ UNLESS((os_data = net_os_alloc_buffer(function_instance, &os_buffer, frame_length))) {
+ return MIN(remainder, frame_length);
+ }
+ os_buffer_used = 0;
+ eem_crc = CRC32_INIT;
+ }
+
+ /* Determine copy length and then copy using memcpy or crc copy as
+ * appropriate.
+ */
+ copy_length = MIN(remainder, frame_length - os_buffer_used);
+
+ //printk(KERN_INFO"%s: YYYY\n", __FUNCTION__);
+ switch (eem_data_type) {
+ case eem_frame_crc_data:
+ eem_crc = crc32_copy(os_buffer + os_buffer_used, urb_buffer, copy_length/* - 1*/, eem_crc);
+ valid_crc = CRC32_GOOD;
+ break;
+ case eem_frame_no_crc_data:
+ memcpy (os_buffer + os_buffer_used, urb_buffer, copy_length);
+ urb_buffer += copy_length - 4;
+ eem_crc = *urb_buffer++ << 24;
+ eem_crc |= *urb_buffer++ << 16;
+ eem_crc |= *urb_buffer++ << 8;
+ eem_crc |= *urb_buffer++;
+ valid_crc = 0xdeadbeef;
+ break;
+ case eem_echo_data:
+ case eem_echo_response_data:
+ memcpy (os_buffer + os_buffer_used, urb_buffer, copy_length);
+ eem_crc = valid_crc = 0;
+ break;
+ default:
+ return copy_length;
+ }
+ //printk(KERN_INFO"%s: ZZZZ\n", __FUNCTION__);
+ urb_buffer += copy_length;
+ remainder -= copy_length;
+ os_buffer_used += copy_length;
+
+ //printk(KERN_INFO"%s: AAAA\n", __FUNCTION__);
+
+ /* Check for incomplete frame and short transfer. This is an
+ * error, as we are expecting more data and EEM Data cannot
+ * cross a bulk transfer boundard (short packet).
+ */
+ if (shortTransfer && (os_buffer_used < frame_length))
+ return copy_length;
+
+ //printk(KERN_INFO"%s: BBBB\n", __FUNCTION__);
+
+ /* Save intermediate information if not complete.
+ */
+ if (os_buffer_used < frame_length) {
+ npd->eem_os_data = os_data;
+ npd->eem_os_buffer = os_buffer;
+ npd->eem_frame_length = frame_length;
+ npd->eem_os_buffer_used = os_buffer_used;
+ npd->eem_data_type = eem_data_type;
+ npd->eem_crc = eem_crc;
+ return copy_length;
+ }
+
+ //printk(KERN_INFO"%s: CCCC\n", __FUNCTION__);
+
+ switch (eem_data_type) {
+ case eem_frame_crc_data:
+ case eem_frame_no_crc_data:
+ //printk(KERN_INFO"%s: DDDD\n", __FUNCTION__);
+ /* Push the frame up as we have completed it. */
+ if ((crc_bad = (valid_crc != eem_crc)))
+ TRACE_MSG3(NTT, "CRC error %08x %08x remainder: %d", eem_crc, valid_crc, remainder);
+ //printk(KERN_INFO"%s: EEEE\n", __FUNCTION__);
+ if (net_fd_recv_buffer(function_instance, os_buffer, copy_length, os_data, crc_bad, 4)) {
+ TRACE_MSG0(NTT, "FAILED");
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ }
+ //printk(KERN_INFO"%s: FFFF\n", __FUNCTION__);
+ break;
+ case eem_echo_data:
+ /* send data back as echo response */
+ if (net_fd_start_echo_eem (function_instance, os_buffer, os_buffer_used, os_data, EEM_ECHO_RESPONSE)) {
+ TRACE_MSG0(NTT, "FAILED");
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ }
+ break;
+ case eem_echo_response_data:
+ /* discard */
+ TRACE_MSG1(NTT, "RESPONSE len: %d", copy_length);
+ break;
+ default:
+ return copy_length;
+ }
+ //printk(KERN_INFO"%s: GGGG\n", __FUNCTION__);
+
+ /* tell caller how much data we processed
+ */
+ return copy_length;
+}
+
+/*! net_fd_recv_urb_eem
+ *
+ * @brief callback to process a received URB
+ * Process incoming stream of EEM packets.
+ *
+ * There are three special cases:
+ *
+ * 1. EEM Data Packet not complete at the end of a bulk transfer.
+ * 2. EEM Command Packet not complete at the end of a bulk transfer.
+ * 3. EEM Packets cannot cross short transfer boundary.
+ *
+ * N.B. EEM 5.1 says "EEM packets shall not be split across USB transfers."
+ * But we cannot determine what the maximum host transfer size is, so the
+ * only hard boundary is if the transfer is terminated with a short packet,
+ * Zero Length EEM Packet (ZLE) or Zero Length Packet (ZLP).
+ *
+ * @param urb received urb
+ * @param rc dummy
+ * @return non-zero for failure.
+ */
+int net_fd_recv_urb_eem(struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ u8 *urb_buffer = urb->buffer;
+ int remainder = urb->actual_length;
+ u32 temmp=0;
+ int endpoint_index = BULK_IN_A;
+
+ int maximumPacketSize = usbd_endpoint_wMaxPacketSize(function_instance, endpoint_index,
+ usbd_high_speed(function_instance));
+ BOOL shortTransfer = (remainder % maximumPacketSize);
+
+ TRACE_MSG2(NTT, "status: %d actual_length: %d", urb->status, urb->actual_length);
+
+ while (remainder) {
+
+ u8 buf0; // first byte of EEM packet header
+ u8 buf1; // second byte of EEM packet header
+ eem_data_type_t eem_data_type = eem_no_data;
+
+ int frame_length, consumed = 0;
+
+ /* Are we in the middle of processing an EEM Data Packet?
+ * Process this data and continue.
+ */
+ if (npd->eem_os_buffer) {
+ consumed -= net_fd_eem_recv_data(function_instance, 0, 0, urb_buffer, remainder, shortTransfer);
+ remainder -= consumed;
+ urb_buffer += consumed;
+ continue;
+ }
+
+ /* Get first byte of EEM packet, there may be a fragment left
+ * from previous transfer.
+ */
+ if (npd->eem_fragment_valid) {
+ buf0 = npd->eem_fragment_data;
+ buf1 = *urb_buffer++;
+ npd->eem_fragment_valid = FALSE;
+ }
+ else {
+ buf0 = *urb_buffer++;
+ buf1 = *urb_buffer++;
+ remainder--;
+
+ /* Check if this is a fragment. save if found
+ */
+ if (shortTransfer && (remainder < 1)) {
+ return 0;
+ }
+ if (remainder < 1) {
+ npd->eem_fragment_valid = TRUE;
+ npd->eem_fragment_data = buf0;
+ return 0;
+ }
+ }
+
+ TRACE_MSG3(NTT, "remainder: %d EEM: %02x %02x", remainder, buf1, buf0);
+
+ /* Check for a Zero Length EEM (ZLE).
+ * A null byte, following by any byte, Skip both and continue if found.
+ */
+ UNLESS (buf0 || buf1) {
+ remainder--;
+ //TRACE_MSG1(NTT, "ZLE: %02x", buf1);
+ continue;
+ }
+
+ /* Check for an EEM Command Packet.
+ */
+ if (buf1 & 0x80) {
+
+ u8 bmEEMCmd = (buf1 >> 3) & 0x7;
+ u8 bmEEMCmdParam = ((buf1 & 0x7) << 8) | buf0;
+ remainder --;
+
+ TRACE_MSG3(NTT, "bmEEMCmd: %d bmEEMCmdParam: %06x remainder: %d", bmEEMCmd, bmEEMCmdParam, remainder);
+
+ /* XXX Still todo...
+ */
+ switch (bmEEMCmd) {
+ /* host sent echo, get data and send back as echo response */
+ case EEM_ECHO:
+ eem_data_type = eem_echo_data;
+ break;
+
+ /* receiving response from echo we sent to host */
+ case EEM_ECHO_RESPONSE:
+ eem_data_type = eem_echo_response_data;
+ break;
+
+ /* host has timed out response complete hint command */
+ case EEM_TICKLE:
+ TRACE_MSG0(NTT, "EEM TICKLE");
+ break;
+
+ /* These are not allowed from Host */
+ case EEM_SUSPEND_HINT:
+ case EEM_RESPONSE_HINT:
+ case EEM_RESPONSE_COMPLETE_HINT:
+ TRACE_MSG1(NTT, "EEM COMMAND - BAD %02x", bmEEMCmd);
+ break;
+
+ }
+ /* if not echo or echo response we are finished */
+ CONTINUE_UNLESS((EEM_ECHO == bmEEMCmd) || (EEM_ECHO_RESPONSE == bmEEMCmd));
+
+ /* echo or echo response, get data length */
+ frame_length = ((buf1 & 0x07) << 8) | buf0;
+ }
+ /* otherwise must be EEM Data */
+ else {
+ /* Must be an EEM Data Packet. Determine frame length,
+ * advance past header and process data.
+ */
+ u32 bmCRC = bmCRC = buf1 & 0x40;
+ frame_length = ((buf1 & 0x3f) << 8) | buf0;
+ remainder--;
+ eem_data_type = bmCRC ? eem_frame_crc_data : eem_frame_no_crc_data;
+ }
+
+ /* process received data - either frame or echo or echo response */
+ consumed = net_fd_eem_recv_data(function_instance, eem_data_type, frame_length, urb_buffer,
+ remainder, shortTransfer);
+ remainder -= consumed;
+ urb_buffer += consumed;
+ }
+
+#if 0 //CONFIG_OTG_LATENCY_CHECK
+ otg_get_ocd_info(NULL, &urb->goto_net_ticks, NULL);
+ //urb->goto_net_ticks=__raw_readl(MXC_GPT_GPTCNT);
+/*Calculate BH waiting time*/
+ LTRACE_MSG4(NTT, "urb pointer:%x, rcv urb:%x, bh start:%x,goto net:%x",urb, urb->urb_rcved_ticks,urb->bh_start_ticks,urb->goto_net_ticks);
+ if ( urb->bh_start_ticks < urb->urb_rcved_ticks ) temmp=(0xffffffff- urb->urb_rcved_ticks + urb->bh_start_ticks);
+ else temmp=urb->bh_start_ticks - urb->urb_rcved_ticks;
+
+ temmp=10000*temmp/LATCH; // latch=10MS, SO temmp should be in uS
+ LTRACE_MSG4(NTT, "urbrcved=>bhstart time(uS):%d, Start:%x, End:%x, LATCH:%x",temmp,urb->urb_rcved_ticks,urb->bh_start_ticks,LATCH);
+
+
+/*Calculate BH process time*/
+ if (urb->goto_net_ticks < urb->bh_start_ticks) temmp=(0xffffffff- urb->bh_start_ticks + urb->goto_net_ticks);
+ else temmp=urb->goto_net_ticks - urb->bh_start_ticks;
+
+ temmp=10000*temmp/LATCH; // latch=10MS, SO temmp should be in uS
+ LTRACE_MSG4(NTT, "bh=>net time(uS):%d, Start:%x, End:%x, LATCH:%x",temmp,urb->bh_start_ticks,urb->goto_net_ticks,LATCH);
+#endif
+ return 0;
+}
+
+/* ********************************************************************************************* */
+#ifdef CONFIG_OTG_NETWORK_START_SINGLE
+#define NETWORK_START_URBS 1
+#else
+#define NETWORK_START_URBS 2
+#endif
+
+/*! net_fd_start_recv_eem - start recv urb(s)
+ *
+ * @param function_instance - pointer to this function instance
+ * @return none
+ */
+int net_fd_start_recv_eem(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int i;
+ int network_start_urbs = NETWORK_START_URBS;
+ int hs = usbd_high_speed(function_instance);
+ int endpoint_index = BULK_OUT_A;
+
+ if (hs)
+ network_start_urbs = NETWORK_START_URBS * 3;
+
+ for (i = 0; i < network_start_urbs; i++) {
+ struct usbd_urb *urb;
+ BREAK_IF(!(urb = usbd_alloc_urb(function_instance, endpoint_index,
+ usbd_endpoint_transferSize(function_instance, endpoint_index, hs),
+ net_fd_recv_urb)));
+ TRACE_MSG5(NTT,"i: %d urb: %p priv: %p npd: %p function: %p",
+ i, urb, urb->function_privdata, npd, function_instance);
+
+ //urb->function_privdata = npd;
+ if (usbd_start_out_urb(urb)) {
+ urb->function_privdata = NULL;
+ usbd_free_urb(urb);
+ }
+ }
+ return 0;
+}
+/* ********************************************************************************************* */
+
+/*! eem_fd_function_enable
+ * @brief enable the function driver
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance - function instance pointer
+ * @return int to indicate operation result
+ */
+
+int eem_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ int rc;
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ rc = net_fd_function_enable(function_instance, network_eem, net_fd_recv_urb_eem,
+ net_fd_start_xmit_eem,
+ net_fd_start_recv_eem, 0);
+ return rc;
+}
+
+/*!
+ * eem_fd_set_configuration - called to indicate set configuration request was received
+ * @param function_instance
+ * @param configuration
+ * @return int
+ */
+int eem_fd_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ char * test = "This is a test.";
+ int rc;
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ rc = net_fd_set_configuration(function_instance, configuration);
+ RETURN_EINVAL_IF(rc);
+ net_fd_start_echo_eem (function_instance, test, strlen(test) + 1, NULL, EEM_ECHO);
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+/*! eem_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations eem_fd_function_ops = {
+ .function_enable = eem_fd_function_enable,
+ .function_disable = net_fd_function_disable,
+
+ .device_request = net_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .set_configuration = eem_fd_set_configuration,
+ .set_interface = NULL,
+ .reset = net_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+
+/*! function driver description
+ */
+struct usbd_interface_driver eem_interface_driver = {
+ .driver = {
+ .name = "cdc-eem-if",
+ .fops = &eem_fd_function_ops, },
+ .interfaces = sizeof (eem_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = eem_interfaces,
+ .endpointsRequested = 2,
+ .requestedEndpoints = cdc_data_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_EEM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_EEM_PROTOCOL,
+ .iFunction = "Belcarra USBLAN - EEM",
+};
+
+/* ********************************************************************************************* */
+
+/*! int eem_mod_init ()
+ * @brief eem_mod_init
+ * @return int
+ */
+int eem_mod_init (void)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return usbd_register_interface_function (&eem_interface_driver, "cdc-eem-if", NULL);
+}
+
+/*! eem_mod_exit()
+* @brief eem_mod_exit
+ * @return none
+ */
+void eem_mod_exit(void)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ usbd_deregister_interface_function (&eem_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_EEM */
+/*!
+ * @brief eem_mod_init
+ * @return int
+ */
+int eem_mod_init (void)
+{
+ return 0;
+}
+
+/*!
+ * @brief eem_mod_exit
+ * @return none
+ */
+void eem_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_EEM */
diff --git a/drivers/otg/functions/network/fermat.c b/drivers/otg/functions/network/fermat.c
new file mode 100644
index 000000000000..9ec0ab27cf48
--- /dev/null
+++ b/drivers/otg/functions/network/fermat.c
@@ -0,0 +1,177 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/fermat.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/fermat.c|20070425221028|05672
+ *
+ * Copyright (c) 2003-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/fermat.c
+ * @brief This implements a special data munging function that
+ * randomizes data. This is a very specific fix for a device
+ * that had trouble with runs of zeros.
+ *
+ * @ingroup NetworkFunction
+ */
+
+#include <otg/otg-compat.h>
+
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+
+#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
+
+#include "fermat.h"
+
+#ifndef FERMAT_DEFINED
+typedef unsigned char BYTE;
+typedef struct fermat {
+ int length;
+ BYTE power[256];
+} FERMAT;
+#endif
+/*! int fermat_setup(FERMAT *, int )
+ * @brief set up fermat table
+ * @param p - pointer to FERMAT
+ * @param seed - seed to generate FERMAT data
+ * @return number of FERMAT length
+ */
+
+static int fermat_setup(FERMAT *p, int seed){
+ int i = 0;
+ unsigned long x,y;
+ y = 1;
+ do{
+ x = y;
+ p->power[i] = ( x == 256 ? 0 : x);
+ y = ( seed * x ) % 257;
+ i += 1;
+ }while( y != 1);
+ p->length = i;
+ return i;
+}
+
+/*! void fermat_xform(FERMAT*, BYTE*, int)
+ * @brief transform data using fermat table
+ * @param p - pointer to fermat data
+ * @param data - pointer to data to transform
+ * @param length - length of data to transform
+ * @return none
+ */
+
+static void fermat_xform(FERMAT *p, BYTE *data, int length){
+ BYTE *pw = p->power;
+ int i, j;
+ BYTE * q ;
+ for(i = 0, j=0, q = data; i < length; i++, j++, q++){
+ if(j>=p->length){
+ j = 0;
+ }
+ *q ^= pw[j];
+ }
+}
+
+static FERMAT default_fermat;
+static const int primitive_root = 5;
+/*! void fermat_init()
+ * @brief initialize fermat using privitive_root as seed
+ *
+ * @return none
+ */
+void fermat_init(){
+ (void) fermat_setup(&default_fermat, primitive_root);
+}
+
+// Here are the public official versions.
+// Change the primitive_root above to another primitive root
+// if you need better scatter. Possible values are 3 and 7
+
+/*! void fermat_encode( BYTE*, int)
+ * @brief using fermat encode data
+ *
+ * @param data - pointer to data to encode
+ * @param length - data length
+ * @return none
+ */
+void fermat_encode(BYTE *data, int length){
+ fermat_xform(&default_fermat, data, length);
+}
+
+/*! void fermat_decode(BYTE* int)
+ * @brief decode data
+ *
+ * @param data - pointer to data to decode
+ * @param length - data lenght
+ * @return none
+ */
+void fermat_decode(BYTE *data, int length){
+ fermat_xform(&default_fermat, data, length);
+}
+
+
+// Note: the seed must be a "primitive root" of 257. This means that
+// the return value of the setup routine must be 256 (otherwise the
+// seed is not a primitive root. The routine will still work fine
+// but will be less pseudo-random.
+
+#undef TEST
+#if TEST
+#include <stdio.h>
+#include <memory.h>
+
+/*! main
+ * @brief - Use FERMAT in two ways: to encode, and to generate test data.
+ */
+
+main(){
+ //Note 3, 5, and 7 are primitive roots of 257
+ // 11 is not a primitive root
+ FERMAT three, five, seven;
+
+ FERMAT three2;
+ printf("Cycle lengths: 3,5,7 %d %d %d \n",
+ fermat_setup(&three, 3),
+ fermat_setup(&five, 5),
+ fermat_setup(&seven, 7));
+ three2=three; // Copy data from three
+ fermat_xform(&three,three2.power,three2.length);
+ fermat_xform(&five,three2.power,three2.length);
+ fermat_xform(&seven,three2.power,three2.length);
+ fermat_xform(&seven,three2.power,three2.length);
+ fermat_xform(&five,three2.power,three2.length);
+ fermat_xform(&three,three2.power,three2.length);
+
+ //At this stage, three2 and three should be identical
+ if(memcpy(&three,&three2,sizeof(FERMAT))){
+ printf("Decoded intact\n");
+ }
+
+ fermat_init();
+ fermat_encode(three2.power,256);
+
+}
+#endif
+
+#endif /* CONFIG_OTG_NETWORK_BLAN_FERMAT */
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/network/fermat.h b/drivers/otg/functions/network/fermat.h
new file mode 100644
index 000000000000..d8a38980d2b5
--- /dev/null
+++ b/drivers/otg/functions/network/fermat.h
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/fermat.h - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/fermat.h|20061218212925|58148
+ *
+ * Copyright (c) 2003-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/fermat.h
+ * @brief Fermat related data structures.
+ *
+ *
+ * @ingroup NetworkFunction
+ */
+
+#ifndef FERMAT_DEFINED
+#define FERMAT_DEFINED 1
+typedef unsigned char BYTE;
+/*! create an alias for fermat
+ * @brief typedef struct fermat FERMAT
+ */
+typedef struct fermat {
+ int length;
+ BYTE power[256];
+} FERMAT;
+
+void fermat_init(void);
+void fermat_encode(BYTE *data, int length);
+void fermat_decode(BYTE *data, int length);
+#endif
diff --git a/drivers/otg/functions/network/net-fd.c b/drivers/otg/functions/network/net-fd.c
new file mode 100644
index 000000000000..7c93d555db62
--- /dev/null
+++ b/drivers/otg/functions/network/net-fd.c
@@ -0,0 +1,906 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/net-fd.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/net-fd.c|20070814184652|40728
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/net-fd.c
+ * @brief The lower edge (USB Device Function) implementation of
+ * the Network Function Driver. This performs the core protocol
+ * handling and data encpasulation.
+ *
+ * This implements the lower edge (USB Device) layer of the Network Function
+ * Driver. Specifically the data encapsulation, envent and protocol handlers.
+ *
+ * This network function driver intended to interoperate with
+ * Belcarra's USBLAN Class drivers.
+ *
+ * These are available for Windows, Linux and Mac OSX. For more
+ * information and to download a copy for testing:
+ *
+ * http://www.belcarra.com/usblan/
+ *
+ * Alternately it should be compatible with any CDC-ECM or CDC-EEM
+ * Class driver.
+ *
+ * Experimental Streaming mode is available for EEM. This simply sends
+ * all frames padded so that they do not terminate the transfer at the
+ * receiving end. The bulk sent callback will send a ZLE frame IFF there
+ * is not pending traffic. This will terminate the transfer at the host.
+ *
+ * The host should attempt to match it's receiving buffer to a size that
+ * optimizes the amount of data without unduly impacting latency. For a
+ * full speed host, something around 35*64 or 2240 bytes would be 1.9mS of
+ * data and allow for about .1mS for servicing.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+//#include <linux/list.h>
+//#include <linux/ctype.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+#include "network.h"
+#include "net-os.h"
+#ifdef CONFIG_OTG_NETWORK_BLAN_FERMAT
+#include "fermat.h"
+#endif
+
+#define TRACE_VERBOSE_SEND 0
+#define TRACE_VERBOSE_RECV 0
+#define TRACE_VERY_VERBOSE 0
+
+static char * local_dev_addr_str;
+static char * remote_dev_addr_str;
+static BOOL override_MAC;
+static int infrastructure_device;
+
+/* ********************************************************************************************** */
+#if !defined (CONFIG_OTG_NETWORK_INTERVAL)
+#define CONFIG_OTG_NETWORK_INTERVAL 1
+#endif /* !defined (CONFIG_OTG_NETWORK_INTERVAL) */
+
+/*! Endpoint Request List
+ */
+
+#if !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+struct usbd_endpoint_request net_fd_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_OUT_A, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_A, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, CONFIG_OTG_NETWORK_INTERVAL, },
+ { 0, },
+};
+u8 net_fd_endpoint_index[ENDPOINTS] = { BULK_OUT_A, BULK_IN_A, INT_IN, };
+
+#elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+struct usbd_endpoint_request net_fd_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_OUT_A, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_A, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_B, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, CONFIG_OTG_NETWORK_INTERVAL, },
+ { 0, },
+};
+u8 net_fd_endpoint_index[ENDPOINTS] = { BULK_OUT_A, BULK_IN_A, BULK_OUT_B, BULK_IN_B, INT_IN, };
+
+
+#elif !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+struct usbd_endpoint_request net_fd_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_OUT_A, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_A, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_OUT_B, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, CONFIG_OTG_NETWORK_INTERVAL, },
+ { 0, },
+};
+u8 net_fd_endpoint_index[ENDPOINTS] = { BULK_OUT_A, BULK_IN_A, BULK_OUT_B, BULK_IN_B, INT_IN, };
+
+
+#elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+struct usbd_endpoint_request net_fd_endpoint_requests[ENDPOINTS+1] = {
+ { BULK_OUT_A, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_A, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_OUT_B, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_B, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, CONFIG_OTG_NETWORK_INTERVAL, },
+ { 0, },
+};
+u8 net_fd_endpoint_index[ENDPOINTS] = { BULK_OUT_A, BULK_IN_A, BULK_OUT_B, BULK_IN_B, INT_IN, };
+
+#endif /* defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT) */
+
+
+/*! Endpoint Request List
+ */
+struct usbd_endpoint_request cdc_data_endpoint_requests[2+1] = {
+ { BULK_OUT_A, 1, 0, 0, USB_DIR_OUT | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { BULK_IN_A, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_BULK, MAXFRAMESIZE + 48, MAXFRAMESIZE + 512, 0, },
+ { 0, },
+};
+u8 cdc_data_endpoint_index[2] = { BULK_OUT_A, BULK_IN_A, };
+
+/*! Endpoint Request List
+ */
+struct usbd_endpoint_request cdc_int_endpoint_requests[1+1] = {
+ { INT_IN, 1, 0, 0, USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT, 16, 64, CONFIG_OTG_NETWORK_INTERVAL, },
+ { 0, },
+};
+
+u8 cdc_int_endpoint_index[1] = { INT_IN, };
+
+
+/* ********************************************************************************************** */
+
+u32 *network_crc32_table;
+
+/*! make_crc_table
+ * @brief Generate the crc32 table
+ *
+ * @return non-zero if malloc fails
+ */
+STATIC int make_crc_table(void)
+{
+ u32 n;
+ RETURN_ZERO_IF(network_crc32_table);
+ RETURN_ENOMEM_IF(!(network_crc32_table = (u32 *)ckmalloc(256*4)));
+ for (n = 0; n < 256; n++) {
+ int k;
+ u32 c = n;
+ for (k = 0; k < 8; k++) {
+ c = (c & 1) ? (CRC32_POLY ^ (c >> 1)) : (c >> 1);
+ }
+ network_crc32_table[n] = c;
+ }
+ return 0;
+}
+
+/* ********************************************************************************************** */
+
+/*! net_fd_urb_sent_int - callback for completed INT URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @param urb - pointer to urb to send
+ * @param urb_rc
+ *
+ * @return non-zero for failure.
+ */
+STATIC int net_fd_urb_sent_int (struct usbd_urb *urb, int urb_rc)
+{
+ //int rc = -EINVAL;
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+
+ //TRACE_MSG3(NTT,"urb: %p npd: %p urb_rc: %d", urb, npd, urb_rc);
+
+ npd->int_urb = NULL;
+ usbd_free_urb (urb);
+ return 0;
+}
+
+/*! net_fd_send_int_notification - send an interrupt notification response
+ *
+ * Generates a response urb on the notification (INTERRUPT) endpoint.
+ *
+ * This is called from either a scheduled task or from the process context
+ * that calls network_open() or network_close().
+ * This must be called with interrupts locked out as net_fd_event_handler can
+ * change the NETWORK_CONFIGURED status
+ *
+ * @param function_instance - pointer to function instance
+ * @param connected - connect status
+ * @param data -
+ *
+ * @return none
+ *
+ */
+void net_fd_send_int_notification(struct usbd_function_instance *function_instance, int connected, int data)
+{
+ struct usb_network_private *npd = function_instance ? function_instance->privdata : NULL;
+ struct usbd_urb *urb;
+ struct cdc_notification_descriptor *cdc;
+ int rc;
+
+ //TRACE_MSG3(NTT,"npd: %p function: %p flags: %04x", npd, function_instance, npd->flags);
+
+ do {
+ BREAK_IF(!function_instance);
+
+ BREAK_IF(npd->network_type != network_blan);
+ BREAK_IF(!npd->have_interrupt);
+
+ BREAK_IF(!(npd->flags & NETWORK_CONFIGURED));
+
+ TRACE_MSG3(NTT,"connected: %d network: %d %d", connected,
+ npd->network_type, network_blan);
+
+ BREAK_IF(usbd_get_device_status(function_instance) != USBD_OK);
+
+ BREAK_IF(!(urb = usbd_alloc_urb (function_instance, INT_IN,
+ sizeof(struct cdc_notification_descriptor), net_fd_urb_sent_int)));
+
+ urb->actual_length = sizeof(struct cdc_notification_descriptor);
+ memset(urb->buffer, 0, sizeof(struct cdc_notification_descriptor));
+
+ cdc = (struct cdc_notification_descriptor *)urb->buffer;
+
+ cdc->bmRequestType = 0xa1;
+
+ if (data) {
+ cdc->bNotification = 0xf0;
+ cdc->wValue = 1;
+ }
+ else {
+ cdc->bNotification = 0x00;
+ cdc->wValue = connected ? 0x01 : 0x00;
+ }
+ cdc->wIndex = 0x00; // XXX interface - check that this is correct
+
+
+ npd->int_urb = urb;
+ TRACE_MSG1(NTT,"int_urb: %p", urb);
+ BREAK_IF (!(rc = usbd_start_in_urb (urb)));
+
+ TRACE_MSG1(NTT,"usbd_start_in_urb failed err: %x", rc);
+ npd->int_urb = NULL;
+ usbd_free_urb (urb);
+
+ } while(0);
+}
+
+/* ********************************************************************************************** */
+
+/*! net_fd_start_xmit - start sending a buffer
+ *
+ * Called with net_os_mutex_enter()d.
+ *
+ * @param function_instance - pointer to function instance
+ * @param buffer - container to data to transmit
+ * @param len - data length
+ * @param data
+ *
+ * @return: 0 if all OK
+ * -EINVAL, -EUNATCH, -ENOMEM
+ * rc from usbd_start_in_urb() if that fails (is != 0, may be one of err values above)
+ * Note: -ECOMM is interpreted by calling routine as signal to leave IF stopped.
+ */
+int net_fd_start_xmit (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct usbd_urb *urb = NULL;
+ int rc;
+
+ if (TRACE_VERBOSE_SEND)
+ TRACE_MSG4(NTT, "os: %p buffer: %p len: %d flags: %04x", data, buffer, len, npd->flags);
+ RETURN_EUNATCH_UNLESS(npd->flags & NETWORK_CONFIGURED);
+ RETURN_EINVAL_IF(usbd_get_device_status(function_instance) != USBD_OK);
+
+ //TRACE_MSG2(NTT, "queued: %d bytes: %d", npd->queued_frames, npd->queued_bytes);
+
+ if (TRACE_VERBOSE_SEND) {
+ TRACE_NSEND(NTT, 32, buffer);
+ if (TRACE_VERY_VERBOSE) {
+ TRACE_SEND(NTT, len, buffer);
+ }
+ }
+
+ UNLESS ((rc = npd->net_start_xmit (function_instance, buffer, len, data)))
+ otg_atomic_inc(&npd->queued_frames);
+
+ return rc;
+}
+
+/* ********************************************************************************************** */
+
+int net_fd_check_tp_response(struct usbd_function_instance *function_instance, u8 *buffer, int length);
+int net_fd_check_rarp_response(struct usbd_function_instance *function_instance, u8 *buffer, int length);
+
+/*! net_fd_recv_buffer - forward a received URB, or clean up after a bad one.
+ *
+ * Common point for transmitting data to net-fd upper OS layer for pushing
+ * to Host OS Network Layer.
+ *
+ * @param function_instance - pointer to functionn instance
+ * @param os_buffer - pointer to os buffer
+ * @param length - os buffer length
+ * @param os_data -
+ * @param crc_bad
+ * @param trim
+ * @return 0 for exception
+ */
+int net_fd_recv_buffer(struct usbd_function_instance *function_instance, u8 *os_buffer, int length,
+ void *os_data, int crc_bad, int trim)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ if (TRACE_VERBOSE_RECV) {
+
+ TRACE_MSG6(NTT, "os_buffer: %x length: %d os_data: %x crc_bad: %d trim: %d flags: %04x",
+ os_buffer, length, os_data, crc_bad, trim, npd->flags);
+
+ TRACE_NRECV(NTT, 32, os_buffer);
+ TRACE_MSG0(NTT, "--");
+ if (TRACE_VERY_VERBOSE) {
+ TRACE_RECV(NTT, length, os_buffer);
+ }
+ }
+
+ #if defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ /* check for RARPD request */
+ if (net_fd_check_rarpd_request(function_instance, os_buffer, length)) {
+
+ TRACE_MSG0(NTT, "RARPD REQUEST");
+
+ /* send reply or ignore and send our own request */
+ ((npd->flags & NETWORK_INFRASTRUCTURE) ?
+ net_fd_send_rarpd_reply : net_fd_send_rarpd_request) (function_instance);
+
+ THROW(handled);
+ }
+
+ /* check for rarpd response IFF not infrastruture */
+ UNLESS ((npd->flags & NETWORK_INFRASTRUCTURE) || !net_fd_check_rarpd_reply(function_instance, os_buffer, length)) {
+ TRACE_MSG0(NTT, "RARPD REPLY RESPONSE");
+
+ /* configure */
+ net_os_config(function_instance);
+ net_os_hotplug(function_instance);
+
+ #if defined(CONFIG_OTG_NETWORK_RFC868_AUTO_CONFIG)
+ net_fd_send_tp_request(function_instance);
+ #endif /* defined(CONFIG_OTG_NETWORK_RFC868_AUTO_CONFIG) */
+ THROW(handled);
+ }
+
+ #if defined(CONFIG_OTG_NETWORK_RFC868_AUTO_CONFIG)
+ UNLESS ((npd->flags & NETWORK_INFRASTRUCTURE) || !net_fd_check_tp_response(function_instance, os_buffer, length)) {
+ TRACE_MSG0(NTT, "TIME PROTOCOL RESPONSE");
+
+ /* set time */
+ net_os_settime(function_instance, ntohl(npd->rfc868time));
+ THROW( handled);
+ }
+
+ #endif /* defined(CONFIG_OTG_NETWORK_RFC868_AUTO_CONFIG) */
+ #endif /* defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+
+ return (net_os_recv_buffer(function_instance, os_data, os_buffer, crc_bad, length, trim));
+
+ CATCH(handled) {
+ /* de-allocate os buffer */
+ net_os_dealloc_buffer(function_instance, os_data, os_buffer);
+ return 0;
+ }
+}
+
+/* ********************************************************************************************** */
+
+/*! net_fd_recv_urb - callback to process a received URB
+ *
+ * @param urb - pointer to copy of received urb,
+ * @param rc - receiving urb result code
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_recv_urb(struct usbd_urb *urb, int rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+
+ TRACE_MSG2(NTT, "status: %d actual_length: %d", urb->status, urb->actual_length);
+
+#ifdef CONFIG_OTG_LATENCY_CHECK
+ otg_get_ocd_info(NULL, &urb->bh_start_ticks, NULL);
+ //urb->bh_start_ticks=__raw_readl(MXC_GPT_GPTCNT);
+ //LTRACE_MSG2(NTT, "Callback urb pointer:%x, ticks:%x",urb,urb->bh_start_ticks);
+
+#endif
+ #if 0
+ TRACE_MSG8(NTT, "[%02x %02x %02x %02x %02x %02x %02x %02x]",
+ urb->buffer[0], urb->buffer[1],
+ urb->buffer[2], urb->buffer[3],
+ urb->buffer[4], urb->buffer[5],
+ urb->buffer[6], urb->buffer[7]
+ );
+ TRACE_MSG8(NTT, "[%02x %02x %02x %02x %02x %02x %02x %02x]",
+ urb->buffer[0+8], urb->buffer[1+8],
+ urb->buffer[2+8], urb->buffer[3+8],
+ urb->buffer[4+8], urb->buffer[5+8],
+ urb->buffer[6+8], urb->buffer[7+8]
+ );
+ #endif
+
+ if (TRACE_VERBOSE_RECV)
+ TRACE_NRECV(NTT, MIN(32, urb->actual_length), urb->buffer);
+
+ if (urb->status == USBD_URB_OK)
+ npd->net_recv_urb(urb, rc);
+
+ urb->status = USBD_OK;
+ return usbd_start_out_urb (urb);
+}
+
+/* ********************************************************************************************** */
+
+/*! net_fd_device_request - process a received SETUP URB
+ *
+ * Processes a received setup packet and CONTROL WRITE data.
+ * Results for a CONTROL READ are placed in urb->buffer.
+ *
+ * @param function_instance - pointer to function instance
+ * @param request - pointer to usbd_device_request
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_device_request (struct usbd_function_instance *function_instance, struct usbd_device_request *request)
+{
+ //struct usb_network_private *npd = function_instance->privdata;
+ //struct usbd_urb *urb;
+ //int index;
+
+ /* Verify that this is a USB Class request per CDC specification or a vendor request.
+ */
+ RETURN_ZERO_IF (!(request->bmRequestType & (USB_REQ_TYPE_CLASS | USB_REQ_TYPE_VENDOR)));
+
+ return -EINVAL;
+}
+
+
+/* ********************************************************************************************** */
+
+
+/*! net_fd_endpoint_cleared -
+ */
+void net_fd_endpoint_cleared (struct usbd_function_instance *function_instance, int bEndpointAddress)
+{
+ //TRACE_MSG1(NTT, "bEndpointAddress: %02x", bEndpointAddress);
+}
+
+
+/*! net_fd_urb_sent_bulk - callback for completed BULK xmit URB
+ *
+ * Handles notification that an urb has been sent (successfully or otherwise).
+ *
+ * @param urb Pointer to the urb that has been sent.
+ * @param urb_rc Result code from the send operation.
+ *
+ * @return non-zero for failure.
+ */
+int net_fd_urb_sent_bulk (struct usbd_urb *urb, int urb_rc)
+{
+ struct usbd_function_instance *function_instance = urb->function_instance;
+ struct usb_network_private *npd = function_instance->privdata;
+ void *buf;
+ int rc = -EINVAL;
+ int endpoint_index = urb->endpoint_index;
+
+
+ npd->avg_queue_frames += otg_atomic_read(&npd->queued_frames);
+ npd->samples++;
+
+ //TRACE_MSG6(NTT,"urb: %p urb_rc: %d length: %d queue: %d avg: %d samples: %d",
+ // urb, urb_rc, urb->actual_length,
+ // npd->queued_frames,
+ // npd->avg_queue_frames / npd->samples,
+ // npd->samples);
+
+ otg_atomic_dec(&npd->queued_frames);
+ otg_atomic_sub(urb->actual_length, &npd->queued_bytes);
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ //otg_atomic_dec(&npd->xmit_urbs_started[endpoint_index]);
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ do {
+
+ BREAK_IF(!urb);
+ buf = urb->function_privdata;
+ urb->function_privdata = NULL;
+
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_IN
+ TRACE_MSG6(NTT,"urb: %p buf: %p endpoint_index: %d buffer: %d alloc: %d actual: %d",
+ urb, buf, endpoint_index,
+ urb->buffer_length,
+ urb->alloc_length,
+ urb->actual_length
+ );
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+ TRACE_MSG7(NTT,"urb: %p buf: %p endpoint_index: %d started: %d buffer: %d alloc: %d actual: %d",
+ urb, buf, endpoint_index,
+ otg_atomic_read(&npd->xmit_urbs_started[endpoint_index]),
+ urb->buffer_length,
+ urb->alloc_length,
+ urb->actual_length
+ );
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_IN */
+
+ net_os_xmit_done(urb->function_instance, buf, urb_rc);
+ #if defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ #else /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+ #ifdef CONFIG_OTG_NETWORK_XMIT_OS
+ urb->buffer = NULL;
+ #endif /* CONFIG_OTG_NETWORK_XMIT_OS */
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_CRC) || defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+ /* Now urb has it's own buffer*/
+ usbd_free_urb (urb);
+ rc = 0;
+
+ } while (0);
+
+ return rc;
+}
+
+
+/* ********************************************************************************************** */
+
+/*! hexdigit -
+ *
+ * Converts characters in [0-9A-F] to 0..15, characters in [a-f] to 42..47, and all others to 0.
+ *
+ * @param c - character to convert
+ * @return the converted decimal value
+ */
+ u8 hexdigit (char c)
+{
+ return isxdigit (c) ? (isdigit (c) ? (c - '0') : (c - 'A' + 10)) : 0;
+}
+
+/*! set_address -generate a device address from mac-address_str
+ * @param mac_address_str - sting used to generate device address
+ * @param dev_addr - pointer to generated device address
+ */
+void set_address(char *mac_address_str, u8 *dev_addr)
+{
+ int i;
+ if (mac_address_str && strlen(mac_address_str)) {
+ for (i = 0; i < NET_ETH_ALEN; i++) {
+ dev_addr[i] =
+ hexdigit (mac_address_str[i * 3]) << 4 |
+ hexdigit (mac_address_str[i * 3 + 1]);
+ }
+ }
+ else {
+ otg_get_random_bytes(dev_addr, NET_ETH_ALEN);
+ dev_addr[0] = (dev_addr[0] & 0xfe) | 0x02;
+ mac_address_str = "RANDOM";
+ }
+ TRACE_MSG7(NTT, "net addr: %02x:%02x:%02x:%02x:%02x:%02x %s",
+ dev_addr[0], dev_addr[1], dev_addr[2],
+ dev_addr[3], dev_addr[4], dev_addr[5],
+ mac_address_str);
+}
+
+/*! net_fd_function_enable - enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance - pointer to this function instance
+ * @param network_type
+ * @param net_recv_urb
+ * @param net_start_xmit
+ * @param net_start_recv
+ * @param recv_urb_flags
+ *
+ * @return int - non-zero for exception
+ */
+
+int net_fd_function_enable (struct usbd_function_instance * function_instance, network_type_t network_type,
+ net_recv_urb_proc_t net_recv_urb,
+ net_start_xmit_proc_t net_start_xmit,
+ net_start_recv_proc_t net_start_recv,
+ u32 recv_urb_flags
+ )
+{
+ struct usb_network_private *npd = NULL;
+
+ /* This will link the usb_network_private structure into function_instance->privdata */
+ net_os_enable(function_instance);
+ npd = function_instance->privdata;
+
+ npd->network_type = network_type;
+ npd->net_recv_urb = net_recv_urb;
+ npd->net_start_xmit = net_start_xmit;
+ npd->net_start_recv = net_start_recv;
+ npd->recv_urb_flags = recv_urb_flags;
+ npd->override_MAC = override_MAC;
+
+ net_os_mutex_enter(function_instance);
+
+ #if 1
+ set_address(local_dev_addr_str, npd->local_dev_addr);
+
+ TRACE_MSG7(NTT, "net npd->local_ addr: %02x:%02x:%02x:%02x:%02x:%02x org MAC string:%s",
+ npd->local_dev_addr[0], npd->local_dev_addr[1], npd->local_dev_addr[2],
+ npd->local_dev_addr[3], npd->local_dev_addr[4], npd->local_dev_addr[5],
+ local_dev_addr_str);
+ npd->local_dev_set = TRUE;
+ #else
+ if (local_dev_addr_str && strlen(local_dev_addr_str)) {
+ set_address(local_dev_addr_str, npd->local_dev_addr);
+ npd->local_dev_set = TRUE;
+ }
+ #endif
+ if (remote_dev_addr_str && strlen(remote_dev_addr_str)) {
+ set_address(remote_dev_addr_str, npd->remote_dev_addr);
+ npd->remote_dev_set = TRUE;
+ }
+
+ npd->have_interrupt = usbd_endpoint_bEndpointAddress(function_instance, INT_IN,
+ usbd_high_speed(function_instance)) ? 1 : 0;
+
+ npd->flags |= NETWORK_ENABLED | (infrastructure_device ? NETWORK_INFRASTRUCTURE : 0);
+
+ net_os_mutex_exit(function_instance);
+ CATCH(error) {
+ // XXX MODULE UNLOCK HERE
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*! net_fd_function_disable - disable the function driver
+ *
+ * @param function_instance - pointer to function instance
+ *
+ * @return none
+ *
+ */
+void net_fd_function_disable (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ net_os_mutex_enter(function_instance);
+ npd->flags &= ~NETWORK_ENABLED;
+ net_os_mutex_exit(function_instance);
+
+ TRACE_MSG0(NTT,"--");
+ net_fd_stop (function_instance);
+
+ if (npd->eem_os_buffer)
+ net_os_dealloc_buffer(function_instance, npd->eem_os_data, npd->eem_os_buffer);
+
+ /* this will disconnect function_instance->privdata */
+ net_os_disable(function_instance);
+
+}
+
+/* ********************************************************************************************** */
+
+extern void net_fd_send_rarp_request(struct usbd_function_instance *function_instance);
+
+/*!
+ * net_fd_start - start network
+ * @param function_instance
+ * @return int
+ */
+int net_fd_start (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int numm;
+ TRACE_MSG0(NTT,"entered");
+ npd->flags |= NETWORK_CONFIGURED;
+
+ if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN))
+ net_os_send_notification_later(function_instance);
+
+ net_os_carrier_on(function_instance);
+
+
+ /* Let the OS layer know, if it's interested. */
+ net_os_config(function_instance);
+ net_os_hotplug(function_instance);
+
+ /* send RARPD request if not an infrastructure device */
+ #if 0
+ #if defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ UNLESS(infrastructure_device)
+ net_fd_send_rarp_request(function_instance);
+ #endif /* defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ #endif
+ TRACE_MSG1(NTT, "CONFIGURED npd->flags: %04x", npd->flags);
+ return 0;
+}
+
+/*!
+ * @brief net_fd_stop - stop network
+ * @param function_instance
+ * @return int
+ */
+int net_fd_stop (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ TRACE_MSG0(NTT,"entered");
+
+ // Return if argument is null.
+
+ // XXX flush
+
+ npd->flags &= ~NETWORK_CONFIGURED;
+ npd->int_urb = NULL;
+
+ // Disable our net-device.
+ // Apparently it doesn't matter if we should do this more than once.
+
+ net_os_carrier_off(function_instance);
+
+ // If we aren't already tearing things down, do it now.
+ if (!(npd->flags & NETWORK_DESTROYING)) {
+ npd->flags |= NETWORK_DESTROYING;
+ //npd->device = NULL;
+ }
+
+ npd->seen_crc = 0;
+
+ // Let the OS layer know, if it's interested.
+ net_os_config(function_instance);
+ net_os_hotplug(function_instance);
+
+ // XXX flush
+ // Release any queued urbs
+ TRACE_MSG1(NTT, "RESET npd->flags: %04x", npd->flags);
+ return 0;
+}
+
+/*!
+ * net_fd_set_configuration - called to indicate set configuration request was received
+ * @param function_instance
+ * @param configuration
+ * @return int
+ */
+int net_fd_set_configuration (struct usbd_function_instance *function_instance, int configuration)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ int hs = usbd_high_speed(function_instance);
+
+ TRACE_MSG2(NTT, "CONFIGURED: %d ip_addr: %08x", configuration, npd->ip_addr);
+
+ if (npd->eem_os_buffer)
+ net_os_dealloc_buffer(function_instance, npd->eem_os_data, npd->eem_os_buffer);
+
+ npd->eem_os_data = npd->eem_os_buffer = NULL;
+ //npd->flags |= NETWORK_CONFIGURED;
+ npd->altsetting = 0;
+ npd->ip_addr = 0;
+ npd->max_recv_urbs = hs ? NETWORK_START_URBS * 3 : NETWORK_START_URBS;
+
+ return net_fd_start(function_instance);
+}
+
+/*!
+ * @brief net_fd_reset - called to indicate bus has been reset
+ * @param function_instance
+ * @return int
+ */
+int net_fd_reset (struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ #ifndef CONFIG_OTG_NETWORK_DOUBLE_OUT
+ int numm = 0;
+ numm = usbd_endpoint_urb_num (function_instance, BULK_OUT_A);
+ TRACE_MSG2(NTT,"RESET %08x urb_number: %d",npd->ip_addr, numm);
+ #else /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ int numm_a = 0;
+ int numm_b = 0;
+ numm_a = usbd_endpoint_urb_num (function_instance, BULK_OUT_A);
+ numm_b = usbd_endpoint_urb_num (function_instance, BULK_OUT_B);
+ TRACE_MSG3(NTT,"RESET %08x urb_number: %d:%d",npd->ip_addr, numm_a, numm_b);
+ #endif /* CONFIG_OTG_NETWORK_DOUBLE_OUT */
+ npd->ip_addr = 0;
+ return net_fd_stop (function_instance);
+}
+
+
+/*!
+ * @brief net_fd_suspended - called to indicate bus has been suspended
+ * @param function_instance
+ * @return int
+ */
+int net_fd_suspended (struct usbd_function_instance *function_instance)
+{
+
+ TRACE_MSG0(NTT, "SUSPENDED");
+ return net_fd_stop (function_instance);
+}
+
+
+/*!
+ * net_fd_resumed - called to indicate bus has been resumed
+ * @param function_instance
+ * @return int
+ */
+int net_fd_resumed (struct usbd_function_instance *function_instance)
+{
+ //struct usbd_interface_instance *interface_instance = (struct usbd_interface_instance *)function_instance;
+
+ TRACE_MSG0(NTT, "RESUMED");
+ return net_fd_start(function_instance);
+}
+
+
+
+/* ********************************************************************************************** */
+
+#if 0
+/*! macstrtest -
+ */
+static int macstrtest(char *mac_address_str)
+{
+ int l = 0;
+
+ if (mac_address_str) {
+ l = strlen(mac_address_str);
+ }
+ return ((l != 0) && (l != 12));
+}
+#endif
+
+/*!
+ * @brief net_fd_init - function driver usb part intialization
+ *
+ * @param info_str
+ * @param local
+ * @param remote
+ * @param override_mac
+ * @param override_personal
+ * @param override_infrastructure
+ * @return non-zero for failure.
+ */
+int net_fd_init(char *info_str, char *local, char *remote, BOOL override_mac, BOOL override_personal, BOOL override_infrastructure)
+{
+ local_dev_addr_str = local;
+ remote_dev_addr_str = remote;
+ if (override_mac) override_MAC= TRUE;
+ #if defined(OTG_NETWORK_INFRASTRUCTURE)
+ infrastructure_device = !override_personal;
+ #else /* defined(OTG_NETWORK_INFRASTRUCTURE) */
+ infrastructure_device = override_infrastructure;
+ #endif /* defined(OTG_NETWORK_INFRASTRUCTURE) */
+
+ TRACE_MSG3(NTT, "local: %s remote: %s Mode; %s Device",
+ local_dev_addr_str ? local_dev_addr_str : "",
+ remote_dev_addr_str ? remote_dev_addr_str : "",
+ infrastructure_device ? "Infrastructure" : "Personal"
+ );
+
+ return make_crc_table();
+}
+
+/*!
+ * @brief net_fd_exit - driver exit
+ *
+ * Cleans up the module. Deregisters the function driver and destroys the network object.
+ */
+void net_fd_exit(void)
+{
+ if (network_crc32_table) {
+ lkfree(network_crc32_table);
+ network_crc32_table = NULL;
+ }
+}
diff --git a/drivers/otg/functions/network/net-ip.c b/drivers/otg/functions/network/net-ip.c
new file mode 100644
index 000000000000..1ae79a8cf01c
--- /dev/null
+++ b/drivers/otg/functions/network/net-ip.c
@@ -0,0 +1,606 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/net-ip.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/net-ip.c|20070425221028|12267
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/functions/network/net-ip.c
+ * @brief Network Function Driver private defines
+ *
+ * This contains support for requesting and checking Time Protocol or RARP
+ * requests and replies.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+
+/* Belcarra public interfaces */
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+
+
+#include "network.h"
+#include "net-os.h"
+
+#define NTT network_fd_trace_tag
+extern otg_tag_t network_fd_trace_tag;
+
+//#define OFFSET(s,e) (&((s *)0)->e)
+
+#define ETH_ALEN 6
+#define IP_ALEN 4
+//#define INADDR_BROADCAST ((u32) 0xffffffff)
+#define IPPROTO_UDP 17
+#define IPVERSION 4
+#define ETHERTYPE_IP 0x0800
+#define ETHERTYPE_ARP 0x0806
+#define ETHERTYPE_RARP 0x8035
+
+#define ARP_ETHERTYPE 0x01
+#define ARP_REQUEST 0x01
+#define ARP_REPLY 0x02
+#define RARP_REQUEST 0x03
+#define RARP_REPLY 0x04
+
+#define RARPD_REQUEST 0x08
+#define RARPD_REPLY 0x09
+#define RARPD_ERROR 0x0a
+
+#define IP_RF 0x8000
+#define IP_DF 0x4000
+#define IP_MF 0x2000
+#define IP_OFFMASK 0x1fff
+
+/* TOD protocol - RFC868 */
+#define TP_PORT 37
+#define RFC868_EPOCH 0x232661280
+#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT
+
+
+
+u8 *mac_bcast_addr = "\xff\xff\xff\xff\xff\xff";
+u8 *mac_zero_addr = "\x00\x00\x00\x00\x00\x00";
+
+u8 *well_known_host_addr = "\x02\x00\x00\x00\x00\x01";
+u8 *well_known_peripheral_addr = "\x02\x00\x00\x00\x00\x00";
+
+/*! @name Structure definitions for each frame section
+ *
+ * @{
+ */
+/*! @struct my_ether_header net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief ether header wrapper
+ */
+struct my_ether_header {
+ u8 ether_dhost[ETH_ALEN];
+ u8 ether_shost[ETH_ALEN];
+ u16 ether_type;
+} __attribute__ ((__packed__));
+
+
+/* ********************************************************************************************** */
+/*! @struct arp_header net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief arp_header struct
+ */
+struct arp_header {
+ u16 arp_hard;
+ u16 arp_prot;
+ u8 arp_hsize;
+ u8 arp_psize;
+ u16 arp_op;
+} __attribute__ ((__packed__));
+
+/*! @struct arp_message net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief arp message struct
+ */
+struct arp_message {
+ u8 arp_sha[ETH_ALEN];
+ u32 arp_sip;
+ u8 arp_tha[ETH_ALEN];
+ u32 arp_tip;
+} __attribute__ ((__packed__));
+
+/*! @struct arp_frame net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief arp frame data structure
+ */
+struct arp_frame {
+ struct my_ether_header eh;
+ struct arp_header aph;
+ struct arp_message apm;
+} __attribute__ ((__packed__));
+
+
+/* ********************************************************************************************** */
+/*! @struct my_ip net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief ip packet structure
+ */
+struct my_ip {
+ union {
+ u8 ip_v; // LSB 4 bits
+ u8 ip_hl; // MSB 4 bits
+ }__attribute__ ((__packed__)) ip;
+ u8 ip_tos;
+ s16 ip_len;
+ s16 ip_id;
+ s16 ip_off;
+ u8 ip_ttl;
+ u8 ip_p;
+ s16 ip_sum;
+ u32 ip_src;
+ u32 ip_dst;
+
+} __attribute__ ((__packed__));
+
+/*! @struct my_udphdr net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief udb header structure
+ */
+struct my_udphdr {
+ u16 uh_sport;
+ u16 uh_dport;
+ u16 uh_ulen;
+ u16 uh_sum;
+} __attribute__ ((__packed__));
+
+/*! @struct tp_message net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief tp message data structure
+ */
+struct tp_message {
+ u32 time;
+} __attribute__ ((__packed__));
+
+/*! @struct tp_frame net-ip.c "otg/functions/network/net-ip.c"
+ *
+ * @brief tp frame data structure
+ */
+struct tp_frame {
+ struct my_ether_header eh;
+ struct my_ip iph;
+ struct my_udphdr udph;
+ struct tp_message tp_message;
+} __attribute__ ((__packed__));
+
+/* @} */
+/* ********************************************************************************************** */
+/*! checksum - compute and return checksum for length length
++ * @param buf - buffer
++ * @param length - buffer length
++ *
++ * @return checksum value
++ */
+
+u16 checksum(void *buf, int length) {
+ u32 sum;
+ u16 *ip = (u16 *) buf;
+ for (sum = 0; length > 1; length -= 2 ) sum += *ip++;
+
+ // process extra byte if there is one
+ if( length > 0 )
+ sum += * (u8 *) ip;
+
+ // munge
+ while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
+
+ return (u16) ~sum;
+}
+
+/* ********************************************************************************************** */
+extern u32 ip_addr;
+extern u32 router_ip;
+extern u32 network_mask;
+extern u32 dns_server_ip;
+extern BOOL do_settime;
+#if defined(LINUX24)
+extern struct timeval net_fd_tv;
+#else
+extern struct timespec net_fd_tv;
+#endif
+
+static u32 ip_id = 1;
+
+
+/* ********************************************************************************************** */
+
+ /*! net_fd_check_tp_response - check for Time Protocol (UDP) response
+ * @param function_instance - function instance pointer
+ * @param buffer - buffer area
+ * @param length - buffer lenght
+ * @return BOOL value
+ */
+BOOL net_fd_check_tp_response(struct usbd_function_instance *function_instance, u8 *buffer, int length)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ struct tp_frame *frame = (struct tp_frame *)buffer;
+
+ unsigned int ip_len;
+
+ struct my_ether_header *eh = (struct my_ether_header *) &frame->eh;
+ struct my_ip *iph = (struct my_ip *) &frame->iph;
+ struct my_udphdr *udph = (struct my_udphdr *) &frame->udph;
+ struct tp_message *tp_message = (struct tp_message *) &frame->tp_message;
+
+
+ //TRACE_MSG1(NTT, "length: %d", length);
+ //TRACE_MSG2(NTT, "length: %d sizeof(my_ether_header): %d", length, sizeof(struct my_ether_header));
+
+ RETURN_FALSE_IF(length < (sizeof(struct my_ether_header) + sizeof(struct my_ip))); /* sanity check */
+
+
+ //TRACE_MSG6(NTT, "ether_dhost: %02x:%02x:%02x:%02x:%02x:%02x", eh->ether_dhost[0], eh->ether_dhost[1],
+ // eh->ether_dhost[2], eh->ether_dhost[3], eh->ether_dhost[4], eh->ether_dhost[5]);
+ //TRACE_MSG6(NTT, "local_addr: %02x:%02x:%02x:%02x:%02x:%02x", npd->local_dev_addr[0], npd->local_dev_addr[1],
+ // npd->local_dev_addr[2], npd->local_dev_addr[3], npd->local_dev_addr[4], npd->local_dev_addr[5]);
+ RETURN_FALSE_IF(memcmp(eh->ether_dhost, npd->local_dev_addr, ETH_ALEN)); /* check if broadcast */
+
+ //TRACE_MSG2(NTT, "ether_type: %04x %04x", ETHERTYPE_IP, ntohs(eh->ether_type));
+ RETURN_FALSE_IF(ETHERTYPE_IP != ntohs(eh->ether_type)); /* check ethernet frame type */
+
+ //TRACE_MSG1(NTT, "ip_v: %04x", iph->ip.ip_v & 0xf0 >> 4 );
+
+ RETURN_FALSE_IF(IPVERSION != (iph->ip.ip_v & 0xf0) >> 4); /* check for ipv4 */
+
+ //TRACE_MSG3(NTT, "ip_hl: %04x %04x %04x", iph->ip.ip_v & 0xf, 5, (length - sizeof(struct my_ether_header) ));
+
+ RETURN_FALSE_IF((iph->ip.ip_hl & 0x0f) < 5); /* check ipv4 header length ok */
+ RETURN_FALSE_IF( ((iph->ip.ip_hl & 0x0f) * 4) > (u8)(length - sizeof(struct my_ether_header)));
+
+ //TRACE_MSG2(NTT, "ip_len: %04x %04x", iph->ip_len, (length - sizeof(struct my_ether_header)));
+ RETURN_FALSE_IF(ntohs(iph->ip_len) > (length - sizeof(struct my_ether_header))); /* check ipv4 total length ok */
+
+ //TRACE_MSG2(NTT, "ip_p: %04x %04x", iph->ip_p, IPPROTO_UDP);
+ RETURN_FALSE_IF(IPPROTO_UDP != iph->ip_p); /* check for UDP */
+
+ udph = (struct my_udphdr *) (((u8*) iph) + ((iph->ip.ip_hl & 0x0f) * 4)); /* check for TP port 37 */
+ //TRACE_MSG2(NTT, "uh_dport: %04x uh_sport: %04x", udph->uh_dport, udph->uh_sport);
+ RETURN_FALSE_IF((TP_PORT != ntohs(udph->uh_dport)) || ((TP_PORT != ntohs(udph->uh_sport))));
+
+ //TRACE_MSG3(NTT, "found port: uh_ulen: %x %x sizeof: %x",
+ // udph->uh_ulen, ntohs(udph->uh_ulen), sizeof(struct tp_message));
+
+ RETURN_FALSE_UNLESS(ntohs(udph->uh_ulen)); /* check if zero length payload */
+ //TRACE_MSG0(NTT, "uh_ulen ok");
+
+ /* get time, convert to Unix time - seconds since midnight 1 jan 1970
+ */
+ npd->rfc868time = tp_message->time;
+ //net_os_settime(function_instance, ntohl(tp_message->time));
+
+ TRACE_MSG0(NTT, "found");
+ return TRUE;
+}
+
+
+/*! net_fd_send_tp_request - allocate and send Time Protocol (UDP) request
+ *
+ * @param function_instance - function instance
+ * @return none
+ */
+void net_fd_send_tp_request(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct tp_frame tp_frame;
+ struct tp_frame *tx = &tp_frame;
+ //struct tp_message *todpmsg = &tp_frame.tp_message;
+
+
+ char buffer[6];
+
+ //TRACE_MSG2(NTT, "sizeof(my_ip) sizeof(tp_frame): %d XXX", sizeof(struct my_ip), sizeof(struct tp_frame));
+
+ memset(&tp_frame, 0, sizeof(struct tp_frame));
+
+ /* ethernet frame - provisionally setup with client mac address */
+ tx->eh.ether_type = htons(ETHERTYPE_IP);
+ memset(tx->eh.ether_dhost, 0xff, ETH_ALEN);
+ memcpy(buffer, npd->local_dev_addr, ETH_ALEN);
+ memcpy(tx->eh.ether_shost, buffer, ETH_ALEN);
+ memcpy(tx->eh.ether_shost, npd->local_dev_addr, ETH_ALEN);
+
+ /* ip header */
+ tx->iph.ip.ip_v = (IPVERSION << 4) | 5;
+ tx->iph.ip_src = htonl(npd->ip_addr);
+ tx->iph.ip_dst = htonl(npd->router_ip);
+ tx->iph.ip_p = IPPROTO_UDP;
+
+
+ /* udp - checksum will be added later */
+ tx->udph.uh_sport = htons(TP_PORT);
+ tx->udph.uh_dport = htons(TP_PORT);
+ tx->udph.uh_ulen = htons(sizeof(struct my_udphdr) + sizeof(struct tp_message));
+
+ //TRACE_MSG6(NTT, "port: %04x %04x %04x:%04x %04x:%04x", tx->udph.uh_sport, tx->udph.uh_dport,
+ // TP_PORT, htons(TP_PORT), 0x1234, htons(0x1234));
+
+ /* udp checksum - we can check sum entire ip packet as rest is zero */
+ tx->udph.uh_sum = checksum( &(tx->iph),
+ sizeof(struct my_ip) + sizeof(struct my_udphdr) + sizeof(struct tp_message));
+
+ /* ip - done here after udp checksum has */
+ tx->iph.ip.ip_hl |= sizeof(struct my_ip) >> 2; // XXX
+ tx->iph.ip.ip_v |= IPVERSION << 4; // XXX
+
+ tx->iph.ip_id = htons(ip_id++);
+ tx->iph.ip_ttl = 0x80;
+
+ tx->iph.ip_len = htons(sizeof(struct my_ip) + sizeof(struct my_udphdr) + sizeof(struct tp_message));
+ tx->iph.ip_sum = checksum(&(tx->iph), sizeof(struct my_ip));
+
+ net_fd_start_xmit(function_instance, (u8 *) tx, sizeof(tp_frame), NULL);
+
+ TRACE_MSG1(NTT, "EXIT: tp_frame: %p", &tp_frame);
+}
+
+/* ********************************************************************************************** */
+
+/*! net_fd_check_rarp_reply - check for RARP (UDP) response
+ *
+ * @param function_instance - function instance
+ * @param buffer - buffer pointer
+ * @param length - buffer length
+ * @return BOOL value
+ */
+BOOL net_fd_check_rarpd_reply(struct usbd_function_instance *function_instance, u8 *buffer, int length)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ struct arp_frame *frame = (struct arp_frame *)buffer;
+
+ //unsigned int ip_len;
+
+
+ struct my_ether_header *eh = (struct my_ether_header *) &frame->eh;
+ struct arp_header *aph = (struct arp_header *) &frame->aph;
+ struct arp_message *apm = (struct arp_message *) &frame->apm;
+
+
+ //TRACE_MSG2(NTT, "length: %d sizeof(my_ether): %d", length, sizeof(struct arp_frame));
+ RETURN_FALSE_IF(length < (sizeof(struct arp_frame))); /* sanity check */
+
+ //TRACE_MSG6(NTT, "ether_dhost: %02x:%02x:%02x:%02x:%02x:%02x",
+ // eh->ether_dhost[0], eh->ether_dhost[1],
+ // eh->ether_dhost[2], eh->ether_dhost[3],
+ // eh->ether_dhost[4], eh->ether_dhost[5]);
+
+ //TRACE_MSG6(NTT, "local_addr: %02x:%02x:%02x:%02x:%02x:%02x",
+ // well_known_peripheral_addr[0], well_known_peripheral_addr[1],
+ // well_known_peripheral_addr[2], well_known_peripheral_addr[3],
+ // well_known_peripheral_addr[4], well_known_peripheral_addr[5]);
+
+ RETURN_FALSE_IF(memcmp(eh->ether_dhost, well_known_peripheral_addr, ETH_ALEN)); /* check if broadcast */
+
+ //TRACE_MSG2(NTT, "ether_type: %04x %04x", ETHERTYPE_RARP, ntohs(eh->ether_type));
+ RETURN_FALSE_IF(ETHERTYPE_RARP != ntohs(eh->ether_type)); /* ethernet frame type RARP */
+
+
+ //TRACE_MSG2(NTT, "arp_hard: %04x %04x", ARP_ETHERTYPE, ntohs(aph->arp_hard));
+ RETURN_FALSE_IF(ARP_ETHERTYPE != ntohs(aph->arp_hard)); /* check hard type */
+
+ //TRACE_MSG2(NTT, "arp_op: %04x %04x", RARPD_REPLY, ntohs(aph->arp_op));
+ RETURN_FALSE_IF(RARPD_REPLY != ntohs(aph->arp_op)); /* check op for RARP request */
+
+ //TRACE_MSG2(NTT, "arp_prot: %04x %04x", ETHERTYPE_IP, ntohs(aph->arp_prot));
+ RETURN_FALSE_IF(ETHERTYPE_IP != ntohs(aph->arp_prot)); /* check prot */
+
+ //TRACE_MSG2(NTT, "hsize: %04x psize: %04x", aph->arp_hsize, aph->arp_psize);
+ RETURN_FALSE_IF(ETH_ALEN != aph->arp_hsize); /* check hard size */
+ RETURN_FALSE_IF(4 != aph->arp_psize); /* check prot size */
+
+ /* check if for us */
+ //TRACE_MSG1(NTT, "arp_tip: %08x", apm->arp_tip);
+ //RETURN_FALSE_IF(apm->arp_tip != htonl(ip_server(oldFrame)));
+
+
+ // XXX what about locally set addr
+ // XXX UNLESS(memcmp(npd->local_dev_addr, mac_zero_addr, ETH_ALEN))
+ // memcpy(npd->local_dev_addr, apm->arp_tha, ETH_ALEN);
+ TRACE_MSG0(NTT, "sos908");
+ if(!(npd->override_MAC)) memcpy(npd->local_dev_addr, apm->arp_tha, ETH_ALEN);
+ //TRACE_MSG6(NTT, "arp_tha: %02x:%02x:%02x:%02x:%02x:%02x", apm->arp_tha[0], apm->arp_tha[1],
+ // apm->arp_tha[2], apm->arp_tha[3], apm->arp_tha[4], apm->arp_tha[5]);
+
+ npd->ip_addr = ntohl(apm->arp_tip);
+ npd->router_ip = ntohl(apm->arp_sip);
+
+ //TRACE_MSG1(NTT, "arp_tip: %08x", apm->arp_tip);
+
+ //net_os_config(function_instance);
+ //net_os_hotplug(function_instance);
+
+ //net_fd_send_tp_request(function_instance);
+
+
+ TRACE_MSG0(NTT, "found");
+ return TRUE;
+}
+
+ /*! net_fd_check_rarp_request - check for RARP (UDP) response
+ *
+ * @param function_instance - function instance
+ * @param buffer - buffer pointer
+ * @param length - buffer length
+ * @return BOOL value
+ */
+BOOL net_fd_check_rarpd_request(struct usbd_function_instance *function_instance, u8 *buffer, int length)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+
+ struct arp_frame *frame = (struct arp_frame *)buffer;
+
+ //unsigned int ip_len;
+
+
+ struct my_ether_header *eh = (struct my_ether_header *) &frame->eh;
+ struct arp_header *aph = (struct arp_header *) &frame->aph;
+ //struct arp_message *apm = (struct arp_message *) &frame->apm;
+
+
+ //TRACE_MSG2(NTT, "length: %d sizeof(my_ether): %d", length, sizeof(struct arp_frame));
+ RETURN_FALSE_IF(length < (sizeof(struct arp_frame))); /* sanity check */
+
+ //TRACE_MSG6(NTT, "ether_dhost: %02x:%02x:%02x:%02x:%02x:%02x", eh->ether_dhost[0], eh->ether_dhost[1],
+ // eh->ether_dhost[2], eh->ether_dhost[3], eh->ether_dhost[4], eh->ether_dhost[5]);
+ //TRACE_MSG6(NTT, "local_addr: %02x:%02x:%02x:%02x:%02x:%02x", npd->local_dev_addr[0], npd->local_dev_addr[1],
+ // npd->local_dev_addr[2], npd->local_dev_addr[3], npd->local_dev_addr[4], npd->local_dev_addr[5]);
+
+ //TRACE_MSG2(NTT, "ether_type: %04x %04x", ETHERTYPE_RARP, ntohs(eh->ether_type));
+
+ RETURN_FALSE_IF(memcmp(eh->ether_dhost, mac_bcast_addr, ETH_ALEN)); /* check if broadcast */
+ RETURN_FALSE_IF(ETHERTYPE_RARP != ntohs(eh->ether_type)); /* ethernet frame type RARP */
+
+
+ TRACE_MSG2(NTT, "arp_hard: %04x %04x", ARP_ETHERTYPE, ntohs(aph->arp_hard));
+ RETURN_FALSE_IF(ARP_ETHERTYPE != ntohs(aph->arp_hard)); /* check hard type */
+
+ TRACE_MSG2(NTT, "arp_op: %04x %04x", RARPD_REPLY, ntohs(aph->arp_op));
+ RETURN_FALSE_IF(RARPD_REQUEST != ntohs(aph->arp_op)); /* check op for RARP request */
+
+ TRACE_MSG2(NTT, "arp_prot: %04x %04x", ETHERTYPE_IP, ntohs(aph->arp_prot));
+ RETURN_FALSE_IF(ETHERTYPE_IP != ntohs(aph->arp_prot)); /* check prot */
+
+ //TRACE_MSG2(NTT, "hsize: %04x psize: %04x", aph->arp_hsize, aph->arp_psize);
+ RETURN_FALSE_IF(ETH_ALEN != aph->arp_hsize); /* check hard size */
+ RETURN_FALSE_IF(4 != aph->arp_psize); /* check prot size */
+
+ /* check if for us */
+ //TRACE_MSG1(NTT, "arp_tip: %08x", apm->arp_tip);
+ //THROW_IF(apm->arp_tip != htonl(ip_server(oldFrame)));
+ //
+
+ //UNLESS(memcmp(npd->local_dev_addr, mac_zero_addr, ETH_ALEN))
+ // memcpy(npd->local_dev_addr, apm->arp_tha, ETH_ALEN);
+
+ //TRACE_MSG6(NTT, "arp_tha: %02x:%02x:%02x:%02x:%02x:%02x", apm->arp_tha[0], apm->arp_tha[1],
+ // apm->arp_tha[2], apm->arp_tha[3], apm->arp_tha[4], apm->arp_tha[5]);
+
+ //npd->ip_addr = ntohl(apm->arp_tip);
+ //npd->router_ip = ntohl(apm->arp_sip);
+
+ //TRACE_MSG1(NTT, "arp_tip: %08x", apm->arp_tip);
+
+ //net_os_config(function_instance);
+ //net_os_hotplug(function_instance);
+
+ //net_fd_send_tp_request(function_instance);
+
+
+ TRACE_MSG0(NTT, "found");
+ return TRUE;
+}
+
+/*! net_fd_send_rarpd_request - allocate and send RARP request
+ *
+ * @param function_instance - function instance
+ * @return none
+ */
+void net_fd_send_rarpd_request(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct arp_frame arp_frame;
+ //struct arp_header *aph = (struct arp_header *) &arp_frame.aph;
+ //struct arp_message *apm = (struct arp_message *) &arp_frame.apm;
+ struct arp_frame *tx = &arp_frame;
+
+ //TRACE_MSG1(NTT, "sizeof(arp_frame): %d XXX", sizeof(struct arp_frame));
+
+ memset(tx, 0, sizeof(struct arp_frame));
+
+ /* copy original frame */
+ //memcpy(tx, rx, sizeof(struct arp_frame));
+
+ /* set mac addresses */
+ memset(tx->eh.ether_dhost, 0xff, ETH_ALEN);
+ memcpy(tx->eh.ether_shost, well_known_peripheral_addr, ETH_ALEN);
+
+ /* set target ethernet addr */
+ memset(tx->apm.arp_tha, 0xff, ETH_ALEN+4);
+ memcpy(tx->apm.arp_sha, npd->local_dev_addr, ETH_ALEN);
+
+ tx->eh.ether_type = htons(ETHERTYPE_RARP);
+ tx->aph.arp_prot = htons(ETHERTYPE_IP );
+ tx->aph.arp_hard = htons(ARP_ETHERTYPE );
+ tx->aph.arp_op = htons(RARPD_REQUEST);
+ tx->aph.arp_hsize = ETH_ALEN;
+ tx->aph.arp_psize = 4;
+ tx->apm.arp_sip = htonl(0);
+ tx->apm.arp_tip = htonl(0);
+
+ // checksum
+
+ net_fd_start_xmit(function_instance, (u8 *) tx, sizeof(arp_frame), NULL);
+
+ TRACE_MSG1(NTT, "frame: %p exit", &arp_frame);
+}
+
+
+ /*! net_fd_send_rarpd_reply - allocate and send RARP request
+ * @param function_instance - function instance
+ * @return none
+ */
+void net_fd_send_rarpd_reply(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = function_instance->privdata;
+ struct arp_frame arp_frame;
+ //struct arp_header *aph = (struct arp_header *) &arp_frame.aph;
+ //struct arp_message *apm = (struct arp_message *) &arp_frame.apm;
+ struct arp_frame *tx = &arp_frame;
+
+ //TRACE_MSG1(NTT, "sizeof(arp_frame): %d XXX", sizeof(struct arp_frame));
+
+ memset(tx, 0, sizeof(struct arp_frame));
+
+ /* copy original frame */
+ //memcpy(tx, rx, sizeof(struct arp_frame));
+
+ /* set mac addresses */
+ memset(tx->eh.ether_dhost, 0xff, ETH_ALEN);
+ memcpy(tx->eh.ether_shost, npd->local_dev_addr, ETH_ALEN);
+
+ /* set target ethernet addr */
+ memset(tx->apm.arp_tha, 0xff, ETH_ALEN+4);
+ memcpy(tx->apm.arp_sha, npd->local_dev_addr, ETH_ALEN);
+
+ tx->eh.ether_type = htons(ETHERTYPE_RARP);
+ tx->aph.arp_prot = htons(ETHERTYPE_IP );
+ tx->aph.arp_hard = htons(ARP_ETHERTYPE );
+ tx->aph.arp_op = htons(RARPD_REQUEST);
+ tx->aph.arp_hsize = ETH_ALEN;
+ tx->aph.arp_psize = 4;
+ tx->apm.arp_sip = htonl(0);
+ tx->apm.arp_tip = htonl(0);
+
+ // checksum
+
+ net_fd_start_xmit(function_instance, (u8 *) tx, sizeof(arp_frame), NULL);
+
+ TRACE_MSG1(NTT, "frame: %p exit", &arp_frame);
+}
+
+/* End of FILE */
diff --git a/drivers/otg/functions/network/net-l24-fix.c b/drivers/otg/functions/network/net-l24-fix.c
new file mode 100644
index 000000000000..c744b46e8be1
--- /dev/null
+++ b/drivers/otg/functions/network/net-l24-fix.c
@@ -0,0 +1,89 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/net-l24-fix.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/net-l24-fix.c|20070814002638|51510
+ *
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*!
+ * @file otg/functions/network/net-l24-fix.c
+ * @brief The Linux 2.4 OS specific upper edge (network interface)
+ * implementation for the Network Function Driver.
+ *
+ * This file implements a standard Linux network driver interface and
+ * the standard Linux 2.4 module init and exit functions.
+ *
+ * If compiled into the kernel, this driver can be used with NFSROOT to
+ * provide the ROOT filesystem. Please note that the kernel NFSROOT support
+ * (circa 2.4.20) can have problems if there are multiple interfaces. So
+ * it is best to ensure that there are no other network interfaces compiled
+ * in.
+ *
+ *
+ * @ingroup NetworkFunction
+ * @ingroup LINUXOS
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/bitops.h>
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/inetdevice.h>
+#include <linux/igmp.h>
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+#include <linux/kmod.h>
+
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <net/ip_fib.h>
+
+
+#if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+extern int devinet_ioctl(unsigned int , void __user *);
+
+int local_devinet_ioctl(unsigned int cmd, void __user *data)
+{
+ return devinet_ioctl(cmd, data);
+}
+
+EXPORT_SYMBOL(local_devinet_ioctl);
+#endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
diff --git a/drivers/otg/functions/network/net-l24-os.c b/drivers/otg/functions/network/net-l24-os.c
new file mode 100644
index 000000000000..30b8da94fea9
--- /dev/null
+++ b/drivers/otg/functions/network/net-l24-os.c
@@ -0,0 +1,1682 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/net-l24-os.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/net-l24-os.c|20070814184652|52070
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*
+ * @file otg/functions/network/net-l24-os.c
+ * @brief The Linux 2.4 OS specific upper edge (network interface)
+ * implementation for the Network Function Driver.
+ *
+ * This file implements a standard Linux network driver interface and
+ * the standard Linux 2.4 module init and exit functions.
+ *
+ * If compiled into the kernel, this driver can be used with NFSROOT to
+ * provide the ROOT filesystem. Please note that the kernel NFSROOT support
+ * (circa 2.4.20) can have problems if there are multiple interfaces. So
+ * it is best to ensure that there are no other network interfaces compiled
+ * in.
+ * @ingroup NetworkFunction
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/otg-compat.h>
+
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+/* OS-specific #includes */
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/inetdevice.h>
+
+
+/* Belcarra public interfaces */
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+
+/* network private interfaces */
+#include "network.h"
+#include "net-os.h"
+
+#define TRACE_VERBOSE 0
+
+MOD_AUTHOR ("sl@belcarra.com, tbr@belcarra.com, balden@belcarra.com");
+
+EMBED_LICENSE();
+
+MOD_DESCRIPTION ("USB Network Function");
+EMBED_USBD_INFO ("network_fd 2.0-beta");
+
+MOD_PARM_STR (local_dev_addr, "Local Device Address", NULL);
+MOD_PARM_STR (remote_dev_addr, "Remote Device Address", NULL);
+MOD_PARM_BOOL (override_mac, "Override MAC address", 0);
+
+MOD_PARM_BOOL (personal_device, "Do not implement an infrastructure device", FALSE);
+MOD_PARM_BOOL (infrastructure_device, "Implement an infrastructure device", FALSE);
+
+int blan_mod_init(void);
+void blan_mod_exit(void);
+
+int basic_mod_init(void);
+void basic_mod_exit(void);
+
+int basic2_mod_init(void);
+void basic2_mod_exit(void);
+
+int ecm_mod_init(void);
+void ecm_mod_exit(void);
+
+int safe_mod_init(void);
+void safe_mod_exit(void);
+
+int eem_mod_init(void);
+void eem_mod_exit(void);
+
+#define NTT network_fd_trace_tag
+otg_tag_t network_fd_trace_tag;
+wait_queue_head_t usb_netif_wq;
+#ifdef CONFIG_OTG_NET_NFS_SUPPORT
+int usb_is_configured;
+#endif
+
+/* Module Parameters ************************************************************************* */
+
+
+/* End of Module Parameters ****************************************************************** */
+
+static u8 zeros[ETH_ALEN];
+
+/* Prevent overlapping of bus administrative functions:
+ *
+ * network_function_enable
+ * network_function_disable
+ * network_hard_start_xmit
+ */
+DECLARE_MUTEX(usbd_network_sem);
+
+struct net_device Network_net_device;
+struct net_device_stats Network_net_device_stats; /* network device statistics */
+struct usb_network_private Network_private;
+
+void notification_schedule_bh (void);
+int network_urb_sent_int (struct usbd_urb *urb, int urb_rc);
+
+static u32 network_router_ip = 0;
+
+extern void usbd_write_info_message(struct usbd_function_instance*, char *msg);
+
+
+//_________________________________________________________________________________________________
+
+/*
+ * Synchronization
+ *
+ *
+ * Notification bottom half
+ *
+ * This is a scheduled task that will send an interrupt notification. Because it
+ * is called from the task scheduler context it needs to verify that the device is
+ * still usable.
+ *
+ * static int network_send_int_blan(struct usbd_simple_instance *, int )
+ * static void notification_bh (void *)
+ * void notification_schedule_bh (void)
+ *
+ *
+ * Netdevice functions
+ *
+ * These are called by the Linux network layer. They must be protected by irq locks
+ * if necessary to prevent interruption by IRQ level events.
+ *
+ * int network_init (struct net_device *net_device)
+ * void network_uninit (struct net_device *net_device)
+ * int network_open (struct net_device *net_device)
+ * int network_stop (struct net_device *net_device)
+ * struct net_device_stats *network_get_stats (struct net_device *net_device)
+ * int network_set_mac_addr (struct net_device *net_device, void *p)
+ * void network_tx_timeout (struct net_device *net_device)
+ * int network_set_config (struct net_device *net_device, struct ifmap *map)
+ * int network_stop (struct net_device *net_device)
+ * int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device)
+ * int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd)
+ *
+ *
+ * Data bottom half functions
+ *
+ * These are called from the bus bottom half handler.
+ *
+ * static int network_recv (struct usb_network_private *, struct net_device *, struct sk_buff *)
+ * int network_recv_urb (struct usbd_urb *)
+ * int network_urb_sent (struct usbd_urb *, int )
+ *
+ *
+ * Hotplug bottom half:
+ *
+ * This is a scheduled task that will send do a hotplug call. Because it is
+ * called from the task scheduler context it needs to verify that the
+ * device is still usable.
+ *
+ * static int hotplug_attach (u32 ip, u32 mask, u32 router, int attach)
+ * static void hotplug_bh (void *data)
+ * void net_os_hotplug (void)
+ *
+ *
+ * Irq level functions:
+ *
+ * These are called at interrupt time do process or respond to USB setup
+ * commands.
+ *
+ * int network_device_request (struct usbd_device_request *)
+ * void network_event_handler (struct usbd_simple_instance *function, usbd_device_event_t event, int data)
+ *
+ *
+ * Enable and disable functions:
+ *
+ * void network_function_enable (struct usbd_simple_instance *, struct usbd_simple_instance *)
+ * void network_function_disable (struct usbd_simple_instance *function)
+ *
+ *
+ * Driver initialization and exit:
+ *
+ * static int network_create (struct usb_network_private *)
+ * static void network_destroy (struct usb_network_private *)
+ *
+ * int network_modinit (void)
+ * void network_modexit (void)
+ */
+
+
+//_______________________________USB part Functions_________________________________________
+
+/*! net_os_mutex_enter - enter mutex region
+ * @param function_instance - pointer to function instance
+ * @return none
+ */
+void net_os_mutex_enter(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ down(&usbd_network_sem);
+}
+
+/* net_os_mutex_exit - exit mutex region
+ * @param function_instance pointer to function instance
+ * @return none
+ */
+void net_os_mutex_exit(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ up(&usbd_network_sem);
+}
+
+/*! notification_bh - Bottom half handler to send a notification status
+ *
+ * Send a notification with open/close status
+ *
+ * It should not be possible for this to be called more than once at a time
+ * as it is only called via schedule_task() which protects against a second
+ * invocation.
+ *
+ * @param data pointer to usbd_function_instance
+ * @return none
+ */
+STATIC void *notification_bh (void *data)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) data;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+ // XXX unsigned long flags;
+ RETURN_NULL_UNLESS(npd);
+ // XXX local_irq_save(flags);
+ net_fd_send_int_notification(function_instance, npd->flags & NETWORK_OPEN, 0);
+ // XXX local_irq_restore(flags);
+ return NULL;
+}
+
+/* notification_schedule_bh - schedule a call for notification_bh
+ *
+ * @param function_instance pointer to function instance
+ * @reutrn none
+ */
+void net_os_send_notification_later(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ RETURN_UNLESS(npd);
+ otg_workitem_start(npd->notification_workitem);
+}
+
+/*! net_os_xmit_done - called from USB part when a transmit completes, good or bad.
+ *
+ * @param function_instance pointer to function instance
+ * @param data pointer passed to fd_ops->start_ximit()
+ * @param tx_rc urb transmit result code, which is USBD_URB_ERROR, USBD_URB_CANCELLED or...
+ * @return non-zero only if network does not exist, ow 0.
+ */
+int net_os_xmit_done(struct usbd_function_instance *function_instance, void *data, int tx_rc)
+{
+ struct sk_buff *skb = (struct sk_buff *) data;
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ struct net_device *net_device = npd ? npd->net_device : NULL;
+ int rc = 0;
+
+ //TRACE_MSG4(NTT, "function: %x npd: %x skb: %x", function_instance, npd, skb, net_device);
+
+ RETURN_ZERO_UNLESS(skb);
+ dev_kfree_skb_any(skb);
+
+ RETURN_ZERO_UNLESS(net_device);
+ if ( net_os_queue_stopped(function_instance))
+ net_os_wake_queue(function_instance);
+
+ RETURN_ZERO_UNLESS(npd);
+
+ switch (tx_rc) {
+ case USBD_URB_ERROR:
+ npd->net_device_stats->tx_errors++;
+ npd->net_device_stats->tx_dropped++;
+ break;
+ case USBD_URB_CANCELLED:
+ npd->net_device_stats->tx_errors++;
+ npd->net_device_stats->tx_carrier_errors++;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*! net_os_dealloc_buffer - deallocate data buffer
+ * @param function_instance pointer to function instance
+ * @param data pointer to data buffer to deallocate
+ * @param buffer * @return none
+ * @return none
+ */
+void net_os_dealloc_buffer(struct usbd_function_instance *function_instance, void *data, void *buffer)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ struct sk_buff *skb = data;
+ RETURN_UNLESS(skb);
+ dev_kfree_skb_any(skb);
+ RETURN_UNLESS(npd);
+ npd->net_device_stats->rx_dropped++;
+}
+
+/*! net_os_alloc_buffer - allocate a buffer
+ * @param function_instance pointer to function instance
+ * @param cp
+ * @param n
+ * @return allocated buffer of void pointer type
+ */
+void *net_os_alloc_buffer(struct usbd_function_instance *function_instance, u8 **cp, int n)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ struct sk_buff *skb;
+
+ /* allocate skb of appropriate length, reserve 2 to align ip
+ */
+ RETURN_NULL_UNLESS ((skb = dev_alloc_skb(n+32)));
+ //TRACE_MSG1(NTT, "skb: %x", skb);
+
+ skb_reserve(skb, 4);
+ while (((int)skb->data) & 0xf)
+ skb_reserve(skb, 1);
+ *cp = skb_put(skb, n);
+
+
+
+ //TRACE_MSG7(NTT, "skb: %x head: %x data: %x tail: %x end: %x len: %d tail-data: %d",
+ // skb, skb->head, skb->data, skb->tail, skb->end, skb->len, skb->tail - skb->data);
+
+ return (void*) skb;
+}
+
+/*! network_recv - function to process an received data URB
+ *
+ * Passes received data to the network layer. Passes skb to network layer.
+ * @param function_instance pointer to function instance
+ * @param net_device pointer to current net device
+ * @param skb pointer to sk_buff struct
+ *
+ *
+ * @return non-zero for failure.
+ */
+STATIC __inline__ int network_recv (struct usbd_function_instance *function_instance,
+ struct net_device *net_device, struct sk_buff *skb)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ int rc;
+
+ RETURN_ZERO_UNLESS(npd);
+#if 0
+ printk(KERN_INFO"%s: len: %x head: %p data: %p tail: %p\n", __FUNCTION__,
+ skb->len, skb->head, skb->data, skb->tail);
+ {
+ u8 *cp = skb->data;
+ int i;
+ for (i = 0; i < skb->len; i++) {
+ if ((i%32) == 0) {
+ printk("\nrx[%2x] ", i);
+ }
+ printk("%02x ", *cp++);
+ }
+ printk("\n");
+ }
+#endif
+
+#if 0
+ TRACE_MSG4(NTT, "len: %x head: %p data: %p tail: %p",
+ skb->len, skb->head, skb->data, skb->tail);
+ {
+ int i;
+
+ for (i = 0; i < skb->len; i += 8) {
+ u8 *cp = skb->data + i;
+ TRACE_MSG8(NTT, "[ %02x %02x %02x %02x %02x %02x %02x %02x]",
+ cp[0], cp[1],
+ cp[2], cp[3],
+ cp[4], cp[5],
+ cp[6], cp[7]
+ );
+ }
+ }
+#endif
+
+ /* refuse if no device present
+ */
+ if (!netif_device_present (net_device)) {
+ TRACE_MSG0(NTT,"device not present");
+ printk(KERN_INFO"%s: device not present\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* refuse if no carrier
+ */
+ if (!netif_carrier_ok (net_device)) {
+ TRACE_MSG0(NTT,"no carrier");
+ printk(KERN_INFO"%s: no carrier\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* refuse if the net device is down
+ */
+ if (!(net_device->flags & IFF_UP)) {
+ TRACE_MSG1(NTT,"USB Net interface is not up net_dev->flags: %x", net_device->flags);
+// printk(KERN_INFO"%s: USB Net interface is not up net_dev->flags: %x\n", __FUNCTION__, net_device->flags);
+ //npd->net_device_stats->rx_dropped++;
+ return -EINVAL;
+ }
+
+ skb->dev = net_device;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans (skb, net_device);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* pass it up to kernel networking layer
+ */
+ if ((rc = netif_rx (skb)))
+ TRACE_MSG1(NTT,"netif_rx rc: %d", rc);
+
+ npd->net_device_stats->rx_bytes += skb->len;
+ npd->net_device_stats->rx_packets++;
+
+ return 0;
+}
+
+/*! net_os_recv_buffer - forward a received URB, or clean up after a bad one.
+ * @param function_instance pointer to function instance
+ * @param os_data == NULL --> just accumulate stats (count 1 bad buff)
+ * @param os_buffer
+ * @param crc_bad != 0 --> count a crc error and free data/skb
+ * @param length
+ * @param trim --> amount to trim from valid skb
+ * @return non-zero for error
+ */
+int net_os_recv_buffer(struct usbd_function_instance *function_instance, void *os_data,
+ void *os_buffer, int crc_bad, int length, int trim)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ struct sk_buff *skb = (struct sk_buff *) os_data;
+
+ RETURN_EAGAIN_UNLESS(npd);
+
+ //TRACE_MSG7(NTT, "skb: %x head: %x data: %x tail: %x end: %x len: %d tail-data: %d",
+ // skb, skb->head, skb->data, skb->tail, skb->end, skb->len, skb->tail - skb->data);
+
+ //TRACE_MSG3(NTT, "npd: %x skb: %x flags: %04x", npd, skb, npd->flags);
+
+ UNLESS (skb) {
+ /* Lower layer never got around to allocating an skb, but
+ * needs to count a packet it can't forward.
+ */
+ npd->net_device_stats->rx_frame_errors++;
+ npd->net_device_stats->rx_errors++;
+ TRACE_MSG0(NTT, "NO SKB");
+ return -EAGAIN;
+ }
+
+#if 0
+ /* There is an skb, either forward it or free it.
+ */
+ if (crc_bad) {
+ npd->net_device_stats->rx_crc_errors++;
+ npd->net_device_stats->rx_errors++;
+ TRACE_MSG0(NTT, "BAD CRC");
+ return -EAGAIN;
+ }
+#endif
+ /* is the network up?
+ */
+ UNLESS (npd->net_device) {
+
+ // Something wrong, free the skb
+ //dev_kfree_skb_any (skb);
+ // The received buffer didn't get forwarded, so...
+ npd->net_device_stats->rx_dropped++;
+ TRACE_MSG0(NTT, "NO NETWORK");
+ return -EAGAIN;
+ }
+
+ /* Trim if necessary and pass up
+ */
+ if (trim)
+ skb_trim(skb, skb->len - trim);
+
+ // 6 6 2 46
+ // 802.3 requires minimum 46 bytes of data,
+ //
+
+ return network_recv(function_instance, npd->net_device, skb);
+}
+
+extern void *config_bh (void *data);
+STATIC void *hotplug_bh (void *data);
+
+/*! net_os_enable - called to enable network device
+ * @param function_instance pointer to function instance
+ *
+ * @return none
+ *
+ */
+void net_os_enable(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd = &Network_private;
+
+ function_instance->privdata = npd;
+
+ TRACE_MSG3(NTT,"npd: %p function: %p, f->p: %p", npd, function_instance, function_instance->privdata);
+
+ memset(&Network_net_device_stats, 0, sizeof Network_net_device_stats);
+ npd->function_instance = function_instance;
+ npd->max_queued_frames = 10;
+ npd->max_queued_bytes = 20000;
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ THROW_UNLESS((npd->config_otgtask = otg_task_init2("netcfg", config_bh, npd, NTT)), error);
+ //npd->config_otgtask->debug = TRUE;
+ otg_task_start(npd->config_otgtask);
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+
+ #ifdef CONFIG_OTG_NETWORK_HOTPLUG
+ THROW_UNLESS(npd->hotplug_workitem = otg_workitem_init("hotplug", hotplug_bh, npd, NTT), error);
+ //npd->hotplug_workitem->debug = TRUE;
+ #endif /* CONFIG_OTG_NETWORK_HOTPLUG */
+
+ THROW_UNLESS(npd->notification_workitem = otg_workitem_init("netint", notification_bh, npd, NTT), error);
+ //npd->notification_workitem->debug = TRUE;
+
+ /* set the network device address from the local device address
+ */
+ //memcpy(npd->net_device->dev_addr, npd->local_dev_addr, ETH_ALEN);
+
+ CATCH(error) {
+ TRACE_MSG0(NTT,"FAILED");
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ if (npd->config_otgtask) {
+ otg_task_exit(npd->config_otgtask);
+ npd->config_otgtask = NULL;
+ }
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ #ifdef CONFIG_OTG_NETWORK_HOTPLUG
+ if (npd->hotplug_workitem) otg_workitem_exit(npd->hotplug_workitem);
+ npd->hotplug_workitem = NULL;
+ #endif /* CONFIG_OTG_NETWORK_HOTPLUG */
+ if (npd->notification_workitem) otg_workitem_exit(npd->notification_workitem);
+ npd->notification_workitem = NULL;
+ }
+}
+
+/*! net_os_disable - called to disable netwok device
+ * @param function_instance pointer to function instance
+ * @return none
+ *
+ */
+extern void net_os_disable(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ RETURN_UNLESS(npd && npd->net_device);
+ //npd->net_device->priv = NULL;
+ //npd->net_device = NULL;
+ function_instance->privdata = NULL;
+ npd->function_instance = NULL;
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ if (npd->config_otgtask) {
+ otg_task_exit(npd->config_otgtask);
+ npd->config_otgtask = NULL;
+ }
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ #ifdef CONFIG_OTG_NETWORK_HOTPLUG
+ if (npd->hotplug_workitem) otg_workitem_exit(npd->hotplug_workitem);
+ npd->hotplug_workitem = NULL;
+ #endif /* CONFIG_OTG_NETWORK_HOTPLUG */
+ if (npd->notification_workitem) otg_workitem_exit(npd->notification_workitem);
+ npd->notification_workitem = NULL;
+}
+
+/*! net_os_carrier_on - ???
+ * @param function_instance pointer to function instance
+ * @return none
+ *
+ */
+void net_os_carrier_on(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ RETURN_UNLESS(npd);
+ netif_carrier_on(npd->net_device);
+ net_os_wake_queue(function_instance);
+#ifdef CONFIG_OTG_NET_NFS_SUPPORT
+ RETURN_UNLESS (usb_is_configured);
+ wake_up(&usb_netif_wq);
+ usb_is_configured = 1;
+#endif
+ //otg_up_work(npd->config_otgtask);
+}
+
+/*! net_os_carrier_off -???
+ * @param function_instance pointer to function instance
+ *
+ * @return none
+ *
+ */
+void net_os_carrier_off(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ RETURN_UNLESS(npd);
+ net_os_stop_queue(function_instance);
+ netif_carrier_off(npd->net_device);
+ //otg_up_work(npd->config_otgtask);
+}
+
+/*! net_os_queue_stopped --???
+ * @param function_instance
+ *
+ * @return result code
+ *
+ */
+int net_os_queue_stopped(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ int rc;
+
+ RETURN_ZERO_UNLESS(npd);
+ rc = netif_queue_stopped(npd->net_device);
+ //TRACE_MSG3(NTT, "stopped: %d stops: %d restarts: %d", rc, npd->stops, npd->restarts);
+ return rc;
+}
+
+/*! net_os_wake_queue -???
+ * @param function_instance function instance pointer
+ * @return none
+ *
+ */
+void net_os_wake_queue(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ RETURN_UNLESS(npd);
+ //TRACE_MSG3(NTT, "stopped: %d stops: %d restarts: %d", netif_queue_stopped(npd->net_device), npd->stops, npd->restarts);
+ netif_wake_queue (npd->net_device);
+ npd->restarts++;
+}
+
+/*! net_os_stop_queue - ???
+ * @param function_instance
+ * @return none
+ *
+ */
+void net_os_stop_queue(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ RETURN_UNLESS(npd);
+ //TRACE_MSG3(NTT, "stopped: %d stops: %d restarts: %d", netif_queue_stopped(npd->net_device), npd->stops, npd->restarts);
+ netif_stop_queue(npd->net_device);
+ npd->stops++;
+}
+
+//_______________________________Network Layer Functions____________________________________
+
+/*
+ * In general because the functions are called from an independant layer it is necessary
+ * to verify that the device is still ok and to lock interrupts out to prevent in-advertant
+ * closures while in progress.
+ */
+
+/*! network_init - Initializes the specified network device.
+ *
+ * @param net_device net_device pointer
+ * @return non-zero for failure.
+ */
+/*! hexdigit -
+ *
+ * Converts characters in [0-9A-F] to 0..15, characters in [a-f] to 42..47, and all others to 0.
+ *
+ * @param c - character to convert
+ * @return the converted decimal value
+ */
+
+extern u8 hexdigit (char c);
+extern void set_address(char *mac_address_str, u8 *dev_addr);
+
+
+STATIC int network_init (struct net_device *net_device)
+{
+
+ TRACE_MSG0(NTT,"no-op");
+ set_address(MODPARM(local_dev_addr), net_device->dev_addr);
+ return 0;
+}
+
+
+/*! network_uninit Uninitializes the specified network device.
+ * @param net_device network device pointer
+ * @return none
+ */
+STATIC void network_uninit (struct net_device *net_device)
+{
+ //TRACE_MSG0(NTT,"no-op");
+ return;
+}
+
+
+/*! network_open Opens the specified network device.
+ *
+ * @param net_device network device pointer
+ * @return non-zero for failure.
+ */
+STATIC int network_open (struct net_device *net_device)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+ // XXX unsigned long flags;
+
+ RETURN_ZERO_UNLESS(function_instance);
+ RETURN_ZERO_UNLESS(npd);
+
+ if (npd){
+
+ npd->flags |= NETWORK_OPEN;
+ memcpy(net_device->dev_addr, npd->local_dev_addr, ETH_ALEN);
+
+ }
+ net_os_wake_queue (function_instance);
+
+ // XXX local_irq_save(flags);
+ net_fd_send_int_notification(function_instance, 1, 0);
+ // XXX local_irq_restore(flags);
+
+#ifdef CONFIG_OTG_NET_NFS_SUPPORT
+ if (!usb_is_configured) {
+ if (!in_interrupt()) {
+ printk(KERN_ERR"Please replug USB cable and then ifconfig host interface.\n");
+ interruptible_sleep_on(&usb_netif_wq);
+ }
+ else {
+ printk(KERN_ERR"Warning! In interrupt\n");
+ }
+ }
+#endif
+ return 0;
+}
+
+
+/*! network_stop Stops the specified network device.
+ *
+ * @param net_device - pointer to network device
+ *
+ * @return non-zero for failure.
+ */
+STATIC int network_stop (struct net_device *net_device)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+ // XXX unsigned long flags;
+
+
+ //TRACE_MSG0(NTT,"-");
+
+ RETURN_ZERO_UNLESS(npd);
+ npd->flags &= ~NETWORK_OPEN;
+ // XXX local_irq_save(flags);
+ net_fd_send_int_notification(function_instance, 0, 0);
+ // XXX local_irq_restore(flags);
+
+ return 0;
+}
+
+
+/*! network_get_stats Gets statistics from the specified network device.
+ *
+ * @param net_device - pointer to network device
+ *
+ * @returns pointer to net_device_stats structure with the required information.
+ */
+STATIC struct net_device_stats *network_get_stats (struct net_device *net_device)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+ //if (npd)
+ // return &npd->stats;
+ //else
+ RETURN_NULL_UNLESS(npd && npd->net_device_stats);
+ return npd->net_device_stats; /* network device statistics */
+}
+
+
+/*! network_set_mac_addr Sets the MAC address of the specified network device. Fails if the device is in use.
+ *
+ * @param net_device pointer to network device
+ * @param p - pointer to sockaddr to asign to this device
+ *
+ * @return non-zero for failure.
+ */
+STATIC int network_set_mac_addr (struct net_device *net_device, void *p)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+ struct sockaddr *addr = p;
+ // XXX unsigned long flags;
+
+ TRACE_MSG0(NTT,"--");
+
+ RETURN_EBUSY_IF(netif_running (net_device));
+ RETURN_ZERO_UNLESS(npd);
+ // XXX local_irq_save(flags);
+ memcpy(net_device->dev_addr, addr->sa_data, net_device->addr_len);
+ memcpy(npd->local_dev_addr, npd->net_device->dev_addr, ETH_ALEN);
+ // XXX local_irq_restore(flags);
+ return 0;
+}
+
+
+/*! network_tx_timeout Tells the specified network device that its current transmit attempt has timed out.
+ * @param net_device pointer to network device
+ * @return none
+ */
+STATIC void network_tx_timeout (struct net_device *net_device)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+#if 0
+ npd->net_device_stats.tx_errors++;
+ npd->net_device_stats.tx_dropped++;
+ usbd_cancel_urb_irq (npd->bus, NULL); // QQSV
+#endif
+#if 0
+ // XXX attempt to wakeup the host...
+ if ((npd->network_type == network_blan) && (npd->flags & NETWORK_OPEN)) {
+ notification_schedule_bh();
+ }
+#endif
+}
+
+
+/** network_set_config Sets the specified network device's configuration. Fails if the device is in use.
+ *
+ * @param net_device pointer to network device
+ * @param map An ifmap structure containing configuration values.
+ * Those values which are non-zero/non-null update the corresponding fields
+ * in net_device.
+ *
+ * @returns non-zero for failure.
+ */
+STATIC int network_set_config (struct net_device *net_device, struct ifmap *map)
+{
+ RETURN_EBUSY_IF(netif_running (net_device));
+ if (map->base_addr)
+ net_device->base_addr = map->base_addr;
+ if (map->mem_start)
+ net_device->mem_start = map->mem_start;
+ if (map->irq)
+ net_device->irq = map->irq;
+ return 0;
+}
+
+
+/*! network_change_mtu Sets the specified network device's MTU. Fails if the new value is larger and
+ * the device is in use.
+ *
+ * @param net_device pointer to network device
+ * @param mtu - transmit unit limit
+ * @return non-zero for failure.
+ */
+STATIC int network_change_mtu (struct net_device *net_device, int mtu)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+ RETURN_EBUSY_IF(netif_running (net_device));
+ RETURN_ZERO_UNLESS(npd);
+ RETURN_EBUSY_IF(mtu > npd->mtu);
+ npd->mtu = mtu;
+ return 0;
+}
+
+//_________________________________________________________________________________________________
+// network_hard_start_xmit
+
+/*! net_os_start_xmit - start sending an skb, with usbd_network_sem already held.
+ * @param skb - ???
+ * @param net_device pointer to network device
+ *
+ * @return non-zero (1) if busy. QQSV - this code always returns 0.
+ */
+STATIC __inline__ int net_os_start_xmit (struct sk_buff *skb, struct net_device *net_device)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) net_device->priv;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+ int rc;
+
+ RETURN_ZERO_UNLESS(npd);
+
+ if (!netif_carrier_ok(net_device)) {
+ dev_kfree_skb_any (skb);
+ npd->net_device_stats->tx_dropped++;
+ return 0;
+ }
+
+ // stop queue, it will be restarted only when we are ready for another skb
+ net_os_stop_queue(function_instance);
+ //npd->stopped++;
+
+ // Set the timestamp for tx timeout
+ net_device->trans_start = jiffies;
+
+ // XXX request IN, should start a timer to resend this.
+ // XXX net_fd_send_int_notification(function_instance, 0, 1);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG8(NTT,"SKB: %p head: %p data: %p tail: %p end: %p headroom: %d len: %d tailroom: %d",
+ skb, skb->head, skb->data, skb->tail, skb->end,
+ (skb->data - skb->head), (skb->tail - skb->data), (skb->end - skb->tail));
+
+ switch ((rc = net_fd_start_xmit(function_instance, skb->data, skb->len, (void*)skb))) {
+ case 0:
+ TRACE_MSG1(NTT,"OK: %d", rc);
+ npd->net_device_stats->tx_packets++;
+ npd->net_device_stats->tx_bytes += skb->len;
+ if ((atomic_read(&npd->queued_frames) < npd->max_queued_frames) &&
+ (atomic_read(&npd->queued_bytes) < npd->max_queued_bytes))
+ net_os_wake_queue (function_instance);
+ break;
+
+ case -EINVAL:
+ case -EUNATCH:
+ TRACE_MSG1(NTT,"not attached, send failed: %d", rc);
+ printk(KERN_ERR"%s: not attached, send failed: %d\n", __FUNCTION__, rc);
+ npd->net_device_stats->tx_errors++;
+ npd->net_device_stats->tx_carrier_errors++;
+ net_os_wake_queue(function_instance);
+ break;
+
+ case -ENOMEM:
+ TRACE_MSG1(NTT,"no mem, send failed: %d", rc);
+ printk(KERN_ERR"%s: no mem, send failed: %d\n", __FUNCTION__, rc);
+ npd->net_device_stats->tx_errors++;
+ npd->net_device_stats->tx_fifo_errors++;
+ net_os_wake_queue(function_instance);
+ break;
+
+ case -ECOMM:
+ TRACE_MSG2(NTT,"comm failure, send failed: %d %p", rc, net_device);
+ printk(KERN_ERR"%s: comm failure, send failed: %d %p\n", __FUNCTION__, rc, net_device);
+ // Leave the IF queue stopped.
+ npd->net_device_stats->tx_dropped++;
+ break;
+
+ }
+ if (0 != rc) {
+ dev_kfree_skb_any (skb);
+ // XXX this is what we should do, blows up on some 2.4.20 kernels
+ // return(NET_XMIT_DROP);
+ return 0;
+ }
+ return 0;
+}
+
+/*!
+ * network_hard_start_xmit - start sending an skb. Called by the OS network layer.
+ * @param skb - ???
+ * @param net_device pointer to network device
+ *
+ * @return non-zero (1) if busy. QQSV - this code always returns 0.
+ */
+STATIC int network_hard_start_xmit (struct sk_buff *skb, struct net_device *net_device)
+{
+ int rc = 0;
+ //down(&usbd_network_sem);
+ rc = net_os_start_xmit(skb, net_device);
+ //up(&usbd_network_sem);
+ return rc;
+}
+
+
+/*! network_do_ioctl - perform an ioctl call
+ *
+ * Carries out IOCTL commands for the specified network device.
+ *
+ * @param net_device network device pointer
+ * @param rp Points to an ifreq structure containing the IOCTL parameter(s).
+ * @param cmd The IOCTL command.
+ *
+ * @return non-zero for failure.
+ */
+STATIC int network_do_ioctl (struct net_device *net_device, struct ifreq *rp, int cmd)
+{
+ return -ENOIOCTLCMD;
+}
+
+//_________________________________________________________________________________________________
+/// @var network_device Network_net_device
+///
+struct net_device Network_net_device = {
+ .get_stats = network_get_stats,
+ .tx_timeout = network_tx_timeout,
+ .do_ioctl = network_do_ioctl,
+ .set_config = network_set_config,
+ .set_mac_address = network_set_mac_addr,
+ .hard_start_xmit = network_hard_start_xmit,
+ .change_mtu = network_change_mtu,
+ .init = network_init,
+ .uninit = network_uninit,
+ .open = network_open,
+ .stop = network_stop,
+ .priv = NULL,
+ .name = "usbl0",
+};
+
+
+//_________________________________________________________________________________________________
+/*! net_os_settime - set os time
+ *
+ * @param function_instance function_insance pointer
+ * @param rfc868seconds
+ * @return none
+ */
+void net_os_settime(struct usbd_function_instance *function_instance, u32 rfc868seconds)
+{
+ #if defined(LINUX24)
+ struct timeval net_fd_tv;
+ #else
+ struct timespec net_fd_tv;
+ #endif
+ /* wIndex and wLength contain RFC868 time - seconds since midnight 1
+ * jan 1900 and convert to Unix time - seconds since midnight 1 jan
+ * 1970
+ */
+ TRACE_MSG1(NTT, "RFC868 seconds: %d", rfc868seconds);
+ memset(&net_fd_tv, 0, sizeof(net_fd_tv));
+ net_fd_tv.tv_sec = rfc868seconds;
+ net_fd_tv.tv_sec -= RFC868_OFFSET_TO_EPOCH;
+ do_settimeofday(&net_fd_tv);
+}
+
+//_________________________________________________________________________________________________
+
+#if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+int local_devinet_ioctl(unsigned int cmd, void __user *data);
+/*! sock_ioctl - perform an ioctl call to inet device
+ * @param cmd - io control command
+ * @param ifreq -
+ * @return result code
+ */
+static int sock_ioctl(u32 cmd, struct ifreq *ifreq)
+{
+ int rc = 0;
+ mm_segment_t save_get_fs = get_fs();
+ TRACE_MSG1(NTT, "cmd: %x", cmd);
+ set_fs(get_ds());
+ rc = local_devinet_ioctl(cmd, ifreq);
+ set_fs(save_get_fs);
+ return rc;
+}
+
+/*! sock_addr - setup a socket address for specified interface
+ * @param ifname - interface name
+ * @param cmd - ioctl command
+ * @param s_addr socket address
+ * @return io control operation result code
+ */
+static int sock_addr(char * ifname, u32 cmd, u32 s_addr)
+{
+ struct ifreq ifreq;
+ struct sockaddr_in *sin = (void *) &(ifreq.ifr_ifru.ifru_addr);
+
+ TRACE_MSG2(NTT, "ifname: %s addr: %x", ifname, ntohl(s_addr));
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname);
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = s_addr;
+
+ return sock_ioctl(cmd, &ifreq);
+}
+
+
+/*! sock_flags - set flags for specified interface
+ * @param ifname - interface name
+ * @param oflags
+ * @param sflags
+ * @param rflags
+ * @return int as result code
+ */
+static int sock_flags(char * ifname, u16 oflags, u16 sflags, u16 rflags)
+{
+ int rc = 0;
+ struct ifreq ifreq;
+
+ TRACE_MSG4(NTT, "ifname: %s oflags: %x s_flags: %x r_flags: %x", ifname, oflags, sflags, rflags);
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname);
+
+ oflags |= sflags;
+ oflags &= ~rflags;
+ ifreq.ifr_flags = oflags;
+
+ TRACE_MSG1(NTT, "-> ifr_flags: %x ", ifreq.ifr_flags);
+
+ THROW_IF ((rc = sock_ioctl(SIOCSIFFLAGS, &ifreq)), error);
+
+ TRACE_MSG1(NTT, "<- ifr_flags: %x ", ifreq.ifr_flags);
+
+ CATCH(error) {
+ TRACE_MSG1(NTT, "ifconfig: cannot get/set interface flags (%d)", rc);
+ return rc;
+ }
+ return rc;
+}
+
+/*! network_attach - called to configure interface dnd evice is attached/deatched
+ *
+ * This will use socket calls to configure the interface to the supplied
+ * ip address and make it active.
+ *
+ * @param function_instance function instance pointer
+ * @param ip ip address
+ * @param make network maks
+ * @param router router ip address
+ * @return 0 on success
+ */
+STATIC int network_attach(struct usbd_function_instance *function_instance, u32 ip, u32 mask, u32 router, int attach)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ int err = 0;
+
+
+ RETURN_ZERO_UNLESS(npd);
+
+ if (attach) {
+ u16 oflags = (npd && npd->net_device) ? npd->net_device->flags : 0;
+ TRACE_MSG5(NTT, "ATTACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach);
+ usbd_write_info_message(function_instance, "NETWORK: ATTACH");
+
+ if (npd->net_device)
+ memcpy(npd->net_device->dev_addr, npd->local_dev_addr, ETH_ALEN);
+
+ /* setup ip address, netwask, and broadcast address */
+ if (ip) {
+ char buf[40];
+ THROW_IF ((err = sock_addr("usbl0", SIOCSIFADDR, htonl(ip))), error);
+ if (mask) {
+ THROW_IF ((err = sock_addr("usbl0", SIOCSIFNETMASK, htonl(mask))), error);
+ THROW_IF ((err = sock_addr("usbl0", SIOCSIFBRDADDR, htonl(ip | ~mask))), error);
+ }
+ /* bring the interface up */
+ THROW_IF ((err = sock_flags("usbl0", oflags, IFF_UP, 0)), error);
+
+ snprintf(buf, sizeof(buf), "NETWORK: usbl0 %d.%d.%d.%d",
+ ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
+
+ usbd_write_info_message(function_instance, buf);
+ }
+ }
+ else {
+ u16 oflags = (npd && npd->net_device) ? npd->net_device->flags : 0;
+ TRACE_MSG5(NTT, "DETACH npd: %x ip: %08x mask: %08x router: %08x attach: %d", npd, ip, mask, router, attach);
+ usbd_write_info_message(function_instance, "NETWORK: DETACH");
+ /* bring the interface down */
+ THROW_IF ((err = sock_addr("usbl0", SIOCSIFADDR, htonl(0))), error);
+ THROW_IF ((err = sock_flags("usbl0", oflags, 0, IFF_UP)), error);
+ }
+
+ /* XXX needs to be fixed for multi-interface */
+ network_router_ip = npd->router_ip;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ TRACE_MSG1(NTT, "ifconfig: cannot configure interface (%d)", err);
+ return err;
+ }
+ return 0;
+}
+
+/*! config_bh() - configure network
+ *
+ * Check connected status and load/unload as appropriate.
+ * @param data pointer to function instance
+ * @return none
+ */
+void *config_bh (void *data)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) data;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+ RETURN_NULL_UNLESS(npd);
+
+ TRACE_MSG5(NTT, "function: %x enabled: %x status: %x state: %x npd->config_status: %d",
+ function_instance,
+ npd->flags & NETWORK_ENABLED,
+ usbd_get_device_status(function_instance),
+ usbd_get_device_state(function_instance),
+ npd->config_status);
+
+#if 1
+ while (TRUE) {
+
+ if (npd->flags & NETWORK_CONFIGURED)
+
+ {
+ TRACE_MSG0(NTT,"call npd->net_start_recv");
+ npd->net_start_recv(function_instance);
+ TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function_instance),
+ usbd_get_device_status(function_instance));
+ if ((config_attached != npd->config_status) && npd->ip_addr) {
+ network_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 1);
+ TRACE_MSG0(NTT,"ATTACH");
+ npd->config_status = config_attached;
+ continue;
+ }
+ }
+ else
+ {
+ if (config_detached != npd->config_status) {
+
+ network_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 0);
+ TRACE_MSG0(NTT,"DETACH");
+ npd->config_status = config_detached;
+ continue;
+ }
+
+ }
+ break;
+ }
+ //sleep(1);
+ return NULL;
+
+#else
+ if ((npd->flags & NETWORK_ENABLED ) &&
+ (function_instance && (USBD_OK == usbd_get_device_status(function_instance))
+ && (STATE_CONFIGURED == usbd_get_device_state(function_instance)) && npd->ip_addr))
+ {
+ TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function_instance),
+ usbd_get_device_status(function_instance));
+
+
+ if (config_attached != npd->config_status) {
+ TRACE_MSG0(NTT,"ATTACH");
+ npd->config_status = config_attached;
+ network_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 1);
+ }
+ return NULL;
+ }
+
+
+ if (config_detached != npd->config_status) {
+ TRACE_MSG0(NTT,"DETACH");
+ npd->config_status = config_detached;
+ network_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 0);
+ }
+ return NULL;
+#endif
+}
+
+/*!
+ * net_os_config - schedule a call to config bottom half
+ *
+ * @param function_instance pointer to function instance
+ *
+ * @return none
+ */
+void net_os_config(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+
+ TRACE_MSG0(NTT, "CONFIG");
+ RETURN_UNLESS(npd);
+ //do_settime = FALSE;
+ otg_up_work(npd->config_otgtask);
+}
+#else /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+void net_os_config(struct usbd_function_instance *function_instance)
+{
+ TRACE_MSG0(NTT, "NOT ENABLED");
+ // Do nothing, config not enabled.
+}
+#endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+//______________________________________ Hotplug Functions ________________________________________
+
+#ifdef CONFIG_OTG_NETWORK_HOTPLUG
+
+#define AGENT "network_fd"
+#ifdef CONFIG_OTG_NETWORK_HOTPLUG_PATH
+static char hotplug_path[] = CONFIG_OTG_NETWORK_HOTPLUG_PATH;
+#else
+static char hotplug_path[]="";
+#endif
+
+/*! hotplug_attach - call hotplug
+ *
+ * @param function_instance function instance pointer
+ * @param ip ip address
+ * @param mask maks addrss
+ * @param router router address
+ * @param attach operation indicator for attach/deatch
+ * @return 0 on success
+ */
+STATIC int hotplug_attach(struct usbd_function_instance *function_instance, u32 ip, u32 mask, u32 router, int attach)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ static int count = 0;
+ char *argv[3];
+ char *envp[10];
+ char ifname[20+12 + IFNAMSIZ];
+ int i;
+ char count_str[20];
+
+ RETURN_EINVAL_UNLESS(npd);
+ RETURN_EINVAL_IF(!hotplug_path[0]);
+
+ argv[0] = hotplug_path;
+ argv[1] = AGENT;
+ argv[2] = 0;
+
+ sprintf (ifname, "INTERFACE=%s", npd->net_device->name);
+ sprintf (count_str, "COUNT=%d", count++);
+
+ i = 0;
+ envp[i++] = "HOME=/";
+ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[i++] = ifname;
+
+
+ if (attach) {
+ unsigned char *cp;
+ char ip_str[20+32];
+ char mask_str[20+32];
+ char router_str[20+32];
+ u32 nh;
+
+ nh = htonl(ip);
+ cp = (unsigned char*) &nh;
+ sprintf (ip_str, "IP=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+ nh = htonl(mask);
+ cp = (unsigned char*) &nh;
+ sprintf (mask_str, "MASK=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+ nh = htonl(router);
+ cp = (unsigned char*) &nh;
+ sprintf (router_str, "ROUTER=%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
+
+ //TRACE_MSG3(NTT, "attach %s %s %s", ifname, ip_str, count_str);
+
+ envp[i++] = "ACTION=attach";
+ envp[i++] = ip_str;
+ envp[i++] = mask_str;
+ envp[i++] = router_str;
+
+ TRACE_MSG3(NTT, "attach ip: %08x mask: %08x router: %08x", ip, mask, router);
+ }
+ else {
+ //TRACE_MSG2(NTT,"detach %s %s", ifname, count_str);
+ envp[i++] = "ACTION=detach";
+ TRACE_MSG3(NTT, "detach ip: %08x mask: %08x router: %08x", ip, mask, router);
+ }
+
+ envp[i++] = count_str;
+ envp[i++] = 0;
+
+ return call_usermodehelper (argv[0], argv, envp,0);
+}
+
+
+/*! hotplug_bh - bottom half handler to call hotplug script to signal ATTACH or DETACH
+ *
+ * Check connected status and load/unload as appropriate.
+ *
+ * It should not be possible for this to be called more than once at a time
+ * as it is only called via schedule_task() which protects against a second
+ * invocation.
+ *
+ * @param data - pointer to data to pass to bottom half handler
+ * @return none
+ */
+STATIC void *hotplug_bh (void *data)
+{
+ struct usb_network_private *npd = (struct usb_network_private *) data;
+ struct usbd_function_instance *function_instance = (struct usbd_function_instance *) npd->function_instance;
+
+ RETURN_NULL_UNLESS(npd);
+
+ function_instance = (struct usbd_function_instance *)npd->function_instance;
+
+ TRACE_MSG2(NTT, "function: %x npd->hotplug_status: %d", function_instance, npd->hotplug_status);
+
+
+ if ((npd->flags & NETWORK_ENABLED ) && (function_instance && (USBD_OK == usbd_get_device_status(function_instance))
+ && (STATE_CONFIGURED == usbd_get_device_state(function_instance))))
+ {
+ TRACE_MSG2(NTT,"BUS state: %d status: %d", usbd_get_device_state(function_instance),
+ usbd_get_device_status(function_instance));
+ if (hotplug_attached != npd->hotplug_status) {
+ TRACE_MSG0(NTT,"ATTACH");
+ npd->hotplug_status = hotplug_attached;
+ hotplug_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 1);
+ }
+ return NULL;
+ }
+
+ if (hotplug_detached != npd->hotplug_status) {
+ TRACE_MSG0(NTT,"DETACH");
+ npd->hotplug_status = hotplug_detached;
+ hotplug_attach (function_instance, npd->ip_addr, npd->network_mask, npd->router_ip, 0);
+ }
+ return NULL;
+}
+
+/*!
+ * net_os_hotplug - schedule a call to hotplug bottom half
+ * @param function_instance function instance pointer
+ * @return none
+ */
+void net_os_hotplug(struct usbd_function_instance *function_instance)
+{
+ struct usb_network_private *npd =
+ (struct usb_network_private *) (function_instance ? function_instance->privdata : NULL);
+ RETURN_UNLESS(npd);
+ #if 0
+ SET_WORK_ARG(npd->hotplug_bh, function_instance);
+ SCHEDULE_WORK(npd->hotplug_bh);
+ #else
+ otg_workitem_start(npd->hotplug_workitem);
+ #endif
+}
+#else /* CONFIG_OTG_NETWORK_HOTPLUG */
+void net_os_hotplug(struct usbd_function_instance *function_instance)
+{
+ TRACE_MSG0(NTT, "NOT ENABLED");
+ // Do nothing, hotplug not configured.
+}
+#endif /* CONFIG_OTG_NETWORK_HOTPLUG */
+
+//_________________________________________________________________________________________________
+
+/*! network_create - create and initialize network private structure
+ *
+ * @returns non-zero for failure.
+ */
+STATIC int network_create(void)
+{
+ TRACE_MSG0(NTT,"entered");
+
+ /* Set some fields to generic defaults and register the network device with the kernel networking code */
+
+ Network_private.net_device = &Network_net_device;
+ Network_private.net_device_stats = &Network_net_device_stats;
+ Network_net_device.priv = &Network_private;
+
+ ether_setup(&Network_net_device);
+ RETURN_EINVAL_IF (register_netdev(&Network_net_device));
+
+ netif_stop_queue(&Network_net_device);
+ netif_carrier_off(&Network_net_device);
+ Network_net_device.flags &= ~IFF_UP;
+
+
+ TRACE_MSG0(NTT,"finis");
+ return 0;
+}
+
+/*! network_destroy - destroy network private struture
+ *
+ * Destroys the network interface referenced by the global variable @c Network_net_device.
+ */
+STATIC void network_destroy(void)
+{
+ netif_stop_queue(&Network_net_device);
+ netif_carrier_off(&Network_net_device);
+ unregister_netdev(&Network_net_device);
+
+ Network_private.net_device = NULL;
+ Network_private.net_device_stats = NULL;
+ Network_net_device.priv = NULL;
+}
+
+
+//______________________________________module_init and module_exit________________________________
+#if defined(CONFIG_PROC_FS) && \
+ (defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG))
+
+/*! network_proc_read_ip - implement proc file system read.
+ * Standard proc file system read function.
+ *
+ * @param file
+ * @param buf -storage for read ip value
+ * @param count
+ * @param pos position from which to read
+ * @return read lenth
+ */
+static ssize_t network_proc_read_ip (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ char *bp;
+ int index = (*pos)++;
+
+ RETURN_ZERO_IF(index);
+
+ // get a page, max 4095 bytes of data...
+ RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE()));
+
+ bp = (char *) page;
+
+ len = sprintf(bp, "%d.%d.%d.%d\n",
+ (network_router_ip >> 24) & 0xff,
+ (network_router_ip >> 16) & 0xff,
+ (network_router_ip >> 8) & 0xff,
+ (network_router_ip) & 0xff
+ );
+
+ if (copy_to_user(buf, (char *) page, len))
+ len = -EFAULT;
+
+ free_page (page);
+ return len;
+}
+
+/*! @var struct file_operations otg_message_proc_swithch_functions
+ */
+static struct file_operations otg_message_proc_switch_functions = {
+ read: network_proc_read_ip,
+};
+#endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+
+
+
+/*! network_modinit - driver intialization,
+ *
+ * @returns non-zero for failure.
+ */
+STATIC int network_modinit (void)
+{
+ #if defined(CONFIG_PROC_FS) && \
+ (defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG))
+ struct proc_dir_entry *p;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+
+ BOOL blan = FALSE, basic = FALSE, basic2 = FALSE, ecm = FALSE, eem = FALSE,
+ safe = FALSE, net_fd = FALSE, rnddis = FALSE, procfs = FALSE;
+
+ NTT = otg_trace_obtain_tag(NULL, "network-if");
+ UNLESS (blan = BOOLEAN(!blan_mod_init())) blan_mod_exit();
+ UNLESS (basic = BOOLEAN(!basic_mod_init())) basic_mod_exit();
+ UNLESS (basic2 = BOOLEAN(!basic2_mod_init())) basic2_mod_exit();
+ UNLESS (ecm = BOOLEAN(!ecm_mod_init())) ecm_mod_exit();
+ UNLESS (safe = BOOLEAN(!safe_mod_init())) safe_mod_exit();
+ THROW_UNLESS (eem = BOOLEAN(!eem_mod_init()), error);
+ /* do we have something enabled? */
+ THROW_UNLESS ((eem || blan || basic || basic2 || ecm || safe),error);
+
+ /* verify that they didn't attempt to override both personal and infrastructure override */
+ THROW_IF (MODPARM(personal_device) && MODPARM(infrastructure_device), error);
+
+ /* check that we have remote address if in infrastructure mode */
+ #if defined(OTG_NETWORK_INFRASTRUCTURE)
+ THROW_UNLESS(MODPARM(personal_device) || MODPARM(remote_dev_addr), error);
+ #else /* defined(OTG_NETWORK_INFRASTRUCTURE) */
+ THROW_UNLESS(!MODPARM(infrastructure_device) || MODPARM(remote_dev_addr), error);
+ #endif /* defined(OTG_NETWORK_INFRASTRUCTURE) */
+
+
+
+ THROW_UNLESS (net_fd = BOOLEAN(!net_fd_init("network",
+ MODPARM(local_dev_addr),
+ MODPARM(remote_dev_addr),
+ MODPARM(override_mac),
+ MODPARM(personal_device),
+ MODPARM(infrastructure_device)
+ )), error);
+
+
+ init_waitqueue_head(&usb_netif_wq);
+
+ #if defined(CONFIG_PROC_FS) && \
+ (defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG))
+ if ((p = create_proc_entry ("network_ip", 0666, 0))) {
+ p->proc_fops = &otg_message_proc_switch_functions;
+ }
+ procfs = TRUE;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+
+ THROW_IF (network_create(), error);
+
+ return 0;
+
+ CATCH(error) {
+ if (net_fd) net_fd_exit();
+
+ #if defined(CONFIG_PROC_FS) && \
+ (defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG))
+ if (procfs) remove_proc_entry("network_ip", NULL);
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ otg_trace_invalidate_tag(NTT);
+ return -EINVAL;
+ }
+}
+
+//_____________________________________________________________________________
+
+/*! network_modexit - driver exit
+ *
+ * Cleans up the module. Deregisters the function driver and destroys the network object.
+ */
+STATIC void network_modexit (void)
+{
+ network_destroy();
+ net_fd_exit();
+
+ #ifdef CONFIG_PROC_FS
+ remove_proc_entry("network_ip", NULL);
+ #endif /* CONFIG_PROC_FS */
+
+ blan_mod_exit();
+ basic_mod_exit();
+ basic2_mod_exit();
+ ecm_mod_exit();
+ safe_mod_exit();
+ eem_mod_exit();
+ //rndis_mod_exit();
+
+
+ otg_trace_invalidate_tag(NTT);
+}
+
+//_________________________________________________________________________________________________
+
+module_init (network_modinit);
+module_exit (network_modexit);
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/functions/network/net-os.h b/drivers/otg/functions/network/net-os.h
new file mode 100644
index 000000000000..4182b21ef8c1
--- /dev/null
+++ b/drivers/otg/functions/network/net-os.h
@@ -0,0 +1,141 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/net-os.h
+ * @(#) sl@belcarra.com|otg/functions/network/net-os.h|20060908200949|09138
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+
+/*!
+ * @file otg/functions/network/net-os.h
+ * @brief Structures and function definitions required for implementation
+ * of the OS specific upper edge (network interface) for the Network
+ * Function Driver.
+ *
+ * A USB network driver is composed of two pieces:
+ * 1) An OS specific piece that handles creating and operating
+ * a network device for the given OS.
+ * 2) A USB specific piece that interfaces either with the host
+ * usbcore layer, or with the device usbdcore layer.
+ *
+ * If the USB piece interfaces with the host usbcore layer you get
+ * a network class driver. If the USB piece interfaces with the usbdcore
+ * layer you get a network function driver.
+ *
+ * This file describes the functions exported by the various net-*-os.c
+ * files (implementing (1)) for use in net-fd.c (2).
+ *
+ *
+ * @ingroup NetworkFunction
+ */
+
+#ifndef NET_OS_H
+#define NET_OS_H 1
+
+/*
+ * net_os_mutex_enter - enter mutex region
+ */
+extern void net_os_mutex_enter(struct usbd_function_instance *);
+
+/*
+ * net_os_mutex_exit - exit mutex region
+ */
+extern void net_os_mutex_exit(struct usbd_function_instance *);
+
+/*
+ * net_os_send_notification_later - schedule a callback to the USB part to send
+ * an INT notification.
+ */
+extern void net_os_send_notification_later(struct usbd_function_instance *);
+
+/*
+ * net_os_xmit_done - called from USB part when a transmit completes, good or bad.
+ * tx_rc is SEND_FINISHED_ERROR, SEND_CANCELLED or...
+ * buff_ctx is the pointer passed to fd_ops->start_xmit().
+ * Return non-zero only if network does not exist, ow 0.
+ */
+extern int net_os_xmit_done(struct usbd_function_instance *function, void *buff_ctx, int tx_rc);
+
+/*
+ *
+ */
+extern void *net_os_alloc_buffer(struct usbd_function_instance *, u8 **cp, int n);
+extern void net_os_dealloc_buffer(struct usbd_function_instance *function_instance, void *data, void *buffer);
+
+/*
+ * net_os_recv_buffer - forward a received URB, or clean up after a bad one.
+ * buff_ctx == NULL --> just accumulate stats (count 1 bad buff)
+ * crc_bad != 0 --> count a crc error and free buff_ctx/skb
+ * trim --> amount to trim from valid buffer (i.e. shorten
+ * from amount requested in net_os_alloc_buff(..... n).
+ * This will be called from interrupt context.
+ */
+extern int net_os_recv_buffer(struct usbd_function_instance *, void *os_data,
+ void *os_buffer, int crc_bad, int length, int trim);
+
+/*
+ * net_os_config - schedule a network hotplug event if the OS supports hotplug.
+ */
+extern void net_os_config(struct usbd_function_instance *);
+
+/*
+ * net_os_settime - schedule a network hotplug event if the OS supports hotplug.
+ */
+extern void net_os_settime(struct usbd_function_instance *, u32);
+
+/*
+ * net_os_hotplug - schedule a network hotplug event if the OS supports hotplug.
+ */
+extern void net_os_hotplug(struct usbd_function_instance *);
+
+/*! net_os_enable
+ *
+ */
+extern void net_os_enable(struct usbd_function_instance *function);
+
+/*! net_os_disable
+ *
+ */
+extern void net_os_disable(struct usbd_function_instance *function);
+
+/*! net_os_carrier_on
+ *
+ */
+extern void net_os_carrier_on(struct usbd_function_instance *);
+
+/*! net_os_carrier_off
+ *
+ */
+extern void net_os_carrier_off(struct usbd_function_instance *);
+
+/*! net_os_queue_stopped
+ *
+ */
+int net_os_queue_stopped(struct usbd_function_instance *function_instance);
+
+/*! net_os_wake_queue
+ *
+ */
+void net_os_wake_queue(struct usbd_function_instance *function_instance);
+
+/*! net_os_stop_queue
+ *
+ */
+void net_os_stop_queue(struct usbd_function_instance *function_instance);
+#endif
diff --git a/drivers/otg/functions/network/network.h b/drivers/otg/functions/network/network.h
new file mode 100644
index 000000000000..bc988ea63adb
--- /dev/null
+++ b/drivers/otg/functions/network/network.h
@@ -0,0 +1,630 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/network.h - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/network.h|20070814184652|05358
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @defgroup NetworkFunction Network CDC Interface Function
+ * @ingroup InterfaceFunctions
+ */
+/*!
+ * @file otg/functions/network/network.h
+ * @brief Network Function Driver private defines
+ *
+ * This defines the internal and private structures required
+ * by the Network Function Driver.
+ *
+ * @ingroup NetworkFunction
+ */
+
+/*! This is a test
+ */
+#ifndef NETWORK_FD_H
+#define NETWORK_FD_H 1
+
+#undef CONFIG_OTG_NETWORK_DOUBLE_OUT
+//#define CONFIG_OTG_NETWORK_DOUBLE_OUT
+#undef CONFIG_OTG_NETWORK_DOUBLE_IN
+//#define CONFIG_OTG_NETWORK_DOUBLE_IN
+
+#undef CONFIG_OTG_NETWORK_HS
+
+#undef CONFIG_OTG_NETWORK_XMIT_OS
+#undef CONFIG_OTG_NETWORK_XMIT_OS
+
+#include <otg/otg-trace.h>
+
+#define NTT network_fd_trace_tag
+extern otg_tag_t network_fd_trace_tag;
+
+#ifdef CONFIG_OTG_NETWORK_START_SINGLE
+#define NETWORK_START_URBS 1
+#else
+#define NETWORK_START_URBS 2
+#endif
+
+
+// Some platforms need to get rid of "static" when using GDB or looking at ksyms
+//#define STATIC
+#define STATIC static
+
+typedef enum network_config_status {
+ config_unknown,
+ config_attached,
+ config_detached
+} network_config_status_t;
+
+typedef enum network_hotplug_status {
+ hotplug_unknown,
+ hotplug_attached,
+ hotplug_detached
+} network_hotplug_status_t;
+
+typedef enum network_type {
+ network_unknown,
+ network_ecm,
+ network_eem,
+ network_blan,
+ network_safe,
+ network_basic,
+ network_basic2,
+} network_type_t;
+
+typedef enum eem_data_type {
+ eem_no_data,
+ eem_frame_crc_data,
+ eem_frame_no_crc_data,
+ eem_echo_data,
+ eem_echo_response_data
+} eem_data_type_t;
+
+#if 0
+struct usb_network_params {
+ // enabling switchs
+ u32 cdc;
+ u32 basic;
+ u32 basic2;
+ u32 safe;
+ u32 blan;
+ // capability flags
+ u32 cdc_capable;
+ u32 basic_capable;
+ u32 basic2_capable;
+ u32 safe_capable;
+ u32 blan_capable;
+ // overrides
+ u32 vendor_id;
+ u32 product_id;
+ char *local_mac_address_str;
+ char *remote_mac_address_str;
+ // other switches
+ u32 ep0test;
+ u32 zeroconf;
+};
+#endif
+
+#define CONFIG_OTG_NETWORK_ALLOW_SETID 1
+
+//#define NETWORK_CREATED 0x01
+//#define NETWORK_REGISTERED 0x02
+
+#define NETWORK_INFRASTRUCTURE 0x01
+#define NETWORK_DESTROYING 0x04
+#define NETWORK_ENABLED 0x08
+#define NETWORK_CONFIGURED 0x10
+#define NETWORK_OPEN 0x20
+
+
+#define MCCI_ENABLE_CRC 0x03
+#define BELCARRA_GETMAC 0x04
+
+#define BELCARRA_SETTIME 0x04
+#define BELCARRA_SETIP 0x05
+#define BELCARRA_SETMSK 0x06
+#define BELCARRA_SETROUTER 0x07
+#define BELCARRA_SETDNS 0x08
+#define BELCARRA_PING 0x09
+#define BELCARRA_SETFERMAT 0x0a
+#define BELCARRA_HOSTNAME 0x0b
+
+
+// RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT
+#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80
+
+#define NET_ETH_ALEN 6
+
+typedef int (*net_recv_urb_proc_t) (struct usbd_urb *urb, int rc);
+typedef int (*net_start_xmit_proc_t) (struct usbd_function_instance *, u8 *, int, void*);
+typedef int (*net_start_recv_proc_t) (struct usbd_function_instance *);
+
+/*! @struct usb_network_private network.h otg/functions/network/networks.h
+ */
+
+struct usb_network_private {
+
+ //struct net_device_stats stats; /* network device statistics */
+
+ int flags;
+ int altsetting;
+ struct net_device *net_device;
+ struct net_device_stats *net_device_stats;
+ struct usbd_function_instance *function_instance;
+ struct usbd_simple_driver *simple_driver;
+ //struct usb_network_params *params;
+ //struct net_usb_services *fd_ops;
+ unsigned int maxtransfer;
+ //rwlock_t rwlock;
+
+ network_config_status_t config_status;
+ network_hotplug_status_t hotplug_status;
+ network_type_t network_type;
+
+ int state;
+
+ int mtu;
+ int use_crc;
+ int seen_crc;
+ int seen_crc_error;
+#if defined(CONFIG_OTG_NETWORK_BLAN_FERMAT)
+ int fermat;
+#endif
+
+ unsigned int stops;
+ unsigned int restarts;
+
+ int max_recv_urbs;
+ otg_atomic_t recv_urbs_started[2];
+ otg_atomic_t xmit_urbs_started[2];
+
+ unsigned int max_queued_frames;
+ unsigned int max_queued_bytes;
+
+ otg_atomic_t queued_frames;
+ otg_atomic_t queued_bytes;
+
+ time_t avg_queue_frames;
+
+ time_t jiffies;
+ unsigned long samples;
+
+ int have_interrupt;
+
+ int data_notify;
+
+ struct usbd_urb *int_urb;
+
+ #if 0
+ struct OLD_WORK_ITEM notification_bh;
+ #if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ struct OLD_WORK_ITEM config_bh;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ #ifdef CONFIG_HOTPLUG
+ struct OLD_WORK_ITEM hotplug_bh;
+ #endif /* CONFIG_HOTPLUG */
+ #else
+ struct otg_workitem *notification_workitem;
+
+ #if !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC)
+ struct otg_task *blan_recv_task;
+ #endif /* !defined(CONFIG_OTG_NETWORK_BLAN_CRC) && !defined(CONFIG_OTG_NETWORK_SAFE_CRC) */
+
+ #if defined(CONFIG_OTG_NETWORK_CONNECT)
+ struct otg_workitem *connect_workitem;
+ #endif /* defined(CONFIG_OTG_NETWORK_CONNECT) */
+ #if defined(CONFIG_OTG_NETWORK_DISCONNECT)
+ struct otg_workitem *disconnect_workitem;
+ #endif /* defined(CONFIG_OTG_NETWORK_DISCONNECT) */
+
+ #if defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG)
+ struct otg_workitem *config_workitem;
+ struct otg_task *config_otgtask;
+ #endif /* defined(CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG) || defined(CONFIG_OTG_NETWORK_RARPD_AUTO_CONFIG) */
+ #ifdef CONFIG_HOTPLUG
+ struct otg_workitem *hotplug_workitem;
+ #endif /* CONFIG_HOTPLUG */
+ #endif
+
+ u8 local_dev_addr[NET_ETH_ALEN];
+ BOOL local_dev_set;
+ u8 remote_dev_addr[NET_ETH_ALEN];
+ BOOL remote_dev_set;
+
+
+ BOOL eem_fragment_valid; // a fragment of eem packet exists
+ u8 eem_fragment_data; // the eem packet fragment
+
+ void *eem_os_data; // partially filled network buffer
+ u8 *eem_os_buffer; // partially filled network buffer
+ int eem_frame_length; // frame length for allocated network buffer
+ int eem_os_buffer_used; // amount of data in network buffer
+
+ eem_data_type_t eem_data_type;
+
+ u8 eem_bmCRC; // bmCRC flag
+ u32 eem_crc; // crc for previous data if bmCRC set
+
+ BOOL eem_send_zle_data; // sent ZLE command byte, still need it's data byte
+
+ u32 ip_addr;
+ u32 router_ip;
+ u32 network_mask;
+ u32 dns_server_ip;
+
+ u32 rfc868time;
+
+ char * local_dev_addr_str;
+ char * remote_dev_addr_str;
+ BOOL override_MAC;
+ int infrastructure_device;
+
+ void *privdata;
+ net_recv_urb_proc_t net_recv_urb;
+ net_start_xmit_proc_t net_start_xmit;
+ net_start_recv_proc_t net_start_recv;
+
+ u32 recv_urb_flags;
+};
+
+// XXX this needs to be co-ordinated with rndis.c maximum's
+#define MAXFRAMESIZE 2000
+
+#if !defined(CONFIG_OTG_MANUFACTURER)
+ #define CONFIG_OTG_MANUFACTURER "Belcarra"
+#endif
+
+
+#if !defined(CONFIG_OTG_SERIAL_NUMBER_STR)
+ #define CONFIG_OTG_SERIAL_NUMBER_STR ""
+#endif
+
+/*
+ * Lineo specific
+ */
+
+#define VENDOR_SPECIFIC_CLASS 0xff
+#define VENDOR_SPECIFIC_SUBCLASS 0xff
+#define VENDOR_SPECIFIC_PROTOCOL 0xff
+
+/*
+ * Lineo Classes
+ */
+#define LINEO_CLASS 0xff
+
+#define LINEO_SUBCLASS_BASIC_NET 0x01
+#define LINEO_SUBCLASS_BASIC_SERIAL 0x02
+
+/*
+ * Lineo Protocols
+ */
+#define LINEO_BASIC_NET_CRC 0x01
+#define LINEO_BASIC_NET_CRC_PADDED 0x02
+
+#define LINEO_BASIC_SERIAL_CRC 0x01
+#define LINEO_BASIC_SERIAL_CRC_PADDED 0x02
+
+
+/*
+ * endpoint and interface indexes
+ */
+
+
+#if !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+#define BULK_OUT_A 0x00
+#define BULK_IN_A 0x01
+#define INT_IN 0x02
+#define ENDPOINTS 0x03
+
+#elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && !defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+#define BULK_OUT_A 0x00
+#define BULK_IN_A 0x01
+#define BULK_IN_B 0x02
+#define INT_IN 0x03
+#define ENDPOINTS 0x04
+
+#elif !defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+#define BULK_OUT_A 0x00
+#define BULK_IN_A 0x01
+#define BULK_OUT_B 0x02
+#define INT_IN 0x03
+#define ENDPOINTS 0x04
+
+#elif defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT)
+#define BULK_OUT_A 0x00
+#define BULK_IN_A 0x01
+#define BULK_OUT_B 0x02
+#define BULK_IN_B 0x03
+#define INT_IN 0x04
+#define ENDPOINTS 0x05
+
+#endif /* defined(CONFIG_OTG_NETWORK_DOUBLE_IN) && defined(CONFIG_OTG_NETWORK_DOUBLE_OUT) */
+
+#if 0
+#ifndef CONFIG_OTG_NETWORK_HS
+#define BULK_OUT 0x00
+#define BULK_IN 0x01
+#define INT_IN 0x02
+#define ENDPOINTS 0x03
+#else /* CONFIG_OTG_NETWORK_HS */
+#define BULK_OUT_A 0x00
+#define BULK_IN_A 0x01
+#define BULK_OUT_B 0x02
+#define BULK_IN_B 0x03
+#define INT_IN 0x04
+#define ENDPOINTS_HS 0x05
+#endif /* CONFIG_OTG_NETWORK_HS */
+#endif
+
+#define COMM_INTF 0x00
+#define DATA_INTF 0x01
+
+extern struct usbd_endpoint_request net_fd_endpoint_requests[ENDPOINTS+1];
+extern u8 net_fd_endpoint_index[ENDPOINTS];
+extern u8 network_requested_endpoints[ENDPOINTS+1];
+extern u16 network_requested_transferSizes[ENDPOINTS+1];
+
+extern struct usbd_endpoint_request cdc_int_endpoint_requests[1+1];
+extern struct usbd_endpoint_request cdc_data_endpoint_requests[2+1];
+
+
+extern u8 cdc_data_endpoint_index[2];
+extern u8 cdc_int_endpoint_index[1];
+
+/* bmDataCapabilities */
+#define BMDATA_CRC 0x01
+#define BMDATA_PADBEFORE 0x02
+#define BMDATA_PADAFTER 0x04
+#define BMDATA_FERMAT 0x08
+#define BMDATA_HOSTNAME 0x10
+
+/* bmNetworkCapabilities */
+#define BMNETWORK_SET_PACKET_OK 0x01
+#define BMNETWORK_NONBRIDGED 0x02
+#define BMNETWORK_DATA_NOTIFY_OK 0x04
+#define BMNETWORK_NO_VENDOR 0x08
+
+
+/*
+ * BLAN Data Plane
+ */
+//#define CONFIG_OTG_NETWORK_PADBYTES 8
+//#define CONFIG_OTG_NETWORK_PADAFTER 1
+//#undef CONFIG_OTG_NETWORK_PADBEFORE
+//#define CONFIG_OTG_NETWORK_CRC 1
+
+
+
+//extern struct usb_network_private Usb_network_private;
+//extern u8 local_dev_addr[ETH_ALEN];
+//extern u8 remote_dev_addr[ETH_ALEN];
+
+extern struct usbd_function_operations net_fd_function_ops;
+
+/*! @struct usbd_class_safe_networking_mdlm_descriptor network.h otg/functions/network/network.h
+ */
+struct usbd_class_safe_networking_mdlm_descriptor {
+ u8 bFunctionLength; // 0x06
+ u8 bDescriptorType; // 0x24
+ u8 bDescriptorSubtype; // 0x13
+ u8 bGuidDescriptorType; // 0x00
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+} __attribute__ ((packed));
+
+/*! @struct usbd_class_blan_networking_mdlm_descriptor network.h otg/functions/network/network.h
+ */
+struct usbd_class_blan_networking_mdlm_descriptor {
+ u8 bFunctionLength; // 0x07
+ u8 bDescriptorType; // 0x24
+ u8 bDescriptorSubtype; // 0x13
+ u8 bGuidDescriptorType; // 0x01
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+ u8 bPad;
+} __attribute__ ((packed));
+
+
+
+#if 0
+#define NETWORK_CREATED 0x01
+#define NETWORK_REGISTERED 0x02
+#define NETWORK_DESTROYING 0x04
+#define NETWORK_ENABLED 0x08
+#define NETWORK_ATTACHED 0x10
+#define NETWORK_OPEN 0x20
+
+
+#define MCCI_ENABLE_CRC 0x03
+#define BELCARRA_GETMAC 0x04
+
+#define BELCARRA_SETTIME 0x04
+#define BELCARRA_SETIP 0x05
+#define BELCARRA_SETMSK 0x06
+#define BELCARRA_SETROUTER 0x07
+#define BELCARRA_SETDNS 0x08
+#define BELCARRA_PING 0x09
+#define BELCARRA_SETFERMAT 0x0a
+#define BELCARRA_HOSTNAME 0x0b
+#define BELCARRA_DATA_NOTIFY 0x0c
+#endif
+
+#define RFC868_OFFSET_TO_EPOCH 0x83AA7E80 // RFC868 - seconds from midnight on 1 Jan 1900 GMT to Midnight 1 Jan 1970 GMT
+
+
+extern u32 *network_crc32_table;
+
+#define CRC32_INIT 0xffffffff // Initial FCS value
+#define CRC32_GOOD 0xdebb20e3 // Good final FCS value
+
+#define CRC32_POLY 0xedb88320 // Polynomial for table generation
+
+#define COMPUTE_FCS(val, c) (((val) >> 8) ^ network_crc32_table[((val) ^ (c)) & 0xff])
+
+
+#if 0
+#if !defined(CONFIG_OTG_NETWORK_CRC_DUFF4) && !defined(CONFIG_OTG_NETWORK_CRC_DUFF8)
+/**
+ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so.
+ *
+ * dst Pointer to the destination memory area.
+ * src Pointer to the source memory area.
+ * len Number of bytes to copy.
+ * val Starting value for the CRC FCS.
+ *
+ * Returns Final value of the CRC FCS.
+ *
+ * @sa crc32_pad
+ */
+static u32 __inline__ crc32_copy (u8 *dst, u8 *src, int len, u32 val)
+{
+ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++));
+ return val;
+}
+
+#endif /* DUFFn */
+#endif
+
+
+
+int net_fd_device_request (struct usbd_function_instance *function_instance, struct usbd_device_request *request);
+void net_fd_event_handler (struct usbd_function_instance *function_instance, usbd_device_event_t event, int data);
+void net_fd_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress);
+int net_fd_set_configuration (struct usbd_function_instance *function, int configuration);
+//int net_fd_set_configuration_blan (struct usbd_function_instance *function, int configuration);
+//int net_fd_set_configuration_cdc (struct usbd_function_instance *function, int configuration);
+//int net_fd_set_interface_cdc (struct usbd_function_instance *function, int wIndex, int altsetting);
+int net_fd_start (struct usbd_function_instance *function);
+int net_fd_stop (struct usbd_function_instance *function);
+int net_fd_reset (struct usbd_function_instance *function);
+int net_fd_suspended (struct usbd_function_instance *function);
+int net_fd_resumed (struct usbd_function_instance *function);
+
+int net_fd_function_enable (struct usbd_function_instance *, network_type_t,
+ net_recv_urb_proc_t , net_start_xmit_proc_t, net_start_recv_proc_t, u32 );
+
+void net_fd_function_disable (struct usbd_function_instance *);
+
+int net_fd_start_xmit (struct usbd_function_instance *, u8 *, int , void *);
+
+void net_fd_send_int_notification(struct usbd_function_instance *, int connected, int data);
+
+void net_fd_exit(void);
+int net_fd_init(char *info_str, char *, char *, BOOL, BOOL, BOOL);
+int net_fd_urb_sent_bulk (struct usbd_urb *urb, int urb_rc);
+
+//int net_fd_start_xmit_eem (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data);
+int net_fd_start_xmit_mdlm (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data);
+//int net_fd_start_xmit_crc (struct usbd_function_instance *function_instance, u8 *buffer, int len, void *data);
+//int net_fd_recv_urb_cdc(struct usbd_urb *urb, int rc);
+int net_fd_recv_urb_mdlm(struct usbd_urb *urb, int rc);
+//int net_fd_recv_urb_eem(struct usbd_urb *urb, int rc);
+
+int net_fd_start_recv_mdlm (struct usbd_function_instance *function_instance);
+int net_fd_start_recv_eem (struct usbd_function_instance *function_instance);
+int net_fd_start_recv_ecm (struct usbd_function_instance *function_instance);
+
+int net_fd_urb_sent_bulk (struct usbd_urb *urb, int urb_rc);
+int net_fd_recv_buffer(struct usbd_function_instance *function_instance, u8 *os_buffer, int length,
+ void *os_data, int crc_bad, int trim);
+
+BOOL net_fd_check_rarpd_reply(struct usbd_function_instance *function_instance, u8 *buffer, int length);
+BOOL net_fd_check_rarpd_request(struct usbd_function_instance *function_instance, u8 *buffer, int length);
+BOOL net_fd_check_tp_response(struct usbd_function_instance *function_instance, u8 *buffer, int length);
+void net_fd_send_rarpd_reply(struct usbd_function_instance *function_instance);
+void net_fd_send_rarpd_request(struct usbd_function_instance *function_instance);
+void net_fd_send_tp_request(struct usbd_function_instance *function_instance);
+
+int net_fd_recv_urb(struct usbd_urb *urb, int rc);
+
+/* crc */
+/*
+ * If the following are defined we implement the crc32_copy routine using
+ * Duff's device. This will unroll the copy loop by either 4 or 8. Do not
+ * use these without profiling to test if it actually helps on any specific
+ * device.
+ */
+#undef CONFIG_OTG_NETWORK_CRC_DUFF4
+#undef CONFIG_OTG_NETWORK_CRC_DUFF8
+
+extern u32 *network_crc32_table;
+
+#define CRC32_INIT 0xffffffff // Initial FCS value
+#define CRC32_GOOD 0xdebb20e3 // Good final FCS value
+
+#define CRC32_POLY 0xedb88320 // Polynomial for table generation
+
+#define COMPUTE_FCS(val, c) (((val) >> 8) ^ network_crc32_table[((val) ^ (c)) & 0xff])
+
+
+/*! crc32_copy
+ * Copies a specified number of bytes, computing the 32-bit CRC FCS as it does so.
+ *
+ * @param dst Pointer to the destination memory area.
+ * @param src Pointer to the source memory area.
+ * @param len Number of bytes to copy.
+ * @param val Starting value for the CRC FCS.
+ *
+ * @return Final value of the CRC FCS.
+ *
+ * @sa crc32_pad
+ */
+static u32 __inline__ crc32_copy (u8 *dst, u8 *src, int len, u32 val)
+{
+ //UNLESS (network_crc32_table)
+ // printk(KERN_INFO"%s:\n", __FUNCTION__);
+ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = *src++));
+ return val;
+}
+
+/*! crc32_nocopy
+ * Computes the 32-bit CRC FCS across a buffer.
+ *
+ * @param src Pointer to the source memory area.
+ * @param len Number of bytes to copy.
+ * @param val Starting value for the CRC FCS.
+ *
+ * @return Final value of the CRC FCS.
+ *
+ * @sa crc32_pad
+ */
+static u32 __inline__ crc32_nocopy (u8 *src, int len, u32 val)
+{
+ //UNLESS (network_crc32_table)
+ // printk(KERN_INFO"%s:\n", __FUNCTION__);
+ for (; len-- > 0; val = COMPUTE_FCS (val, *src++));
+ return val;
+}
+
+
+/*! crc32_pad - pad and calculate crc32
+ *
+ * @return CRC FCS
+ */
+static u32 __inline__ crc32_pad (u8 *dst, int len, u32 val)
+{
+ for (; len-- > 0; val = COMPUTE_FCS (val, *dst++ = '\0'));
+ return val;
+}
+
+
+
+#endif /* NETWORK_FD_H */
diff --git a/drivers/otg/functions/network/network_fd.agent b/drivers/otg/functions/network/network_fd.agent
new file mode 100644
index 000000000000..acb81e812231
--- /dev/null
+++ b/drivers/otg/functions/network/network_fd.agent
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Network hotplug policy agent for Linux 2.4 kernels
+#
+# Kernel NET hotplug params include:
+#
+# ACTION=%s [register or unregister]
+# INTERFACE=%s
+#
+# HISTORY:
+#
+
+#echo $0: $* $INTERFACE $ACTION $IP $COUNT >> /tmp/NETWORK_FD
+#set >> /tmp/NETWORK_FD
+
+#exit 0
+
+cd /etc/hotplug
+
+if [ "$INTERFACE" = "" ]; then
+ exit 0
+fi
+
+case $ACTION in
+attach)
+
+ case $INTERFACE in
+
+ usbl*)
+ if [ "$IP" != "0.0.0.0" ] ; then
+
+ ifconfig $INTERFACE $IP up
+
+ elif which pump > /dev/null ; then
+
+ pump -i $INTERFACE
+
+ elif which udhcpc > /dev/null ; then
+
+ udhcpc -i $INTERFACE &
+
+ else
+ ifconfig $INTERFACE 192.168.1.1 up
+ fi
+
+ ;;
+
+ usbb*)
+ ifconfig $INTERFACE 192.168.1.1 up
+ ;;
+
+ esac
+ ;;
+
+
+detach)
+
+ case $INTERFACE in
+
+ usbl*)
+ ifconfig $INTERFACE 0.0.0.0 down
+ ;;
+
+ usbb*)
+ ifconfig $INTERFACE 0.0.0.0 down
+ ;;
+
+ esac
+ ;;
+
+*)
+
+ exit 0
+ ;;
+
+esac
+
+/etc/rc.d/init.d/S91samba stop
+sleep 2
+/etc/rc.d/init.d/S91samba start
+
+
+exit 0
diff --git a/drivers/otg/functions/network/otg-config.h b/drivers/otg/functions/network/otg-config.h
new file mode 100644
index 000000000000..b46de0600b4c
--- /dev/null
+++ b/drivers/otg/functions/network/otg-config.h
@@ -0,0 +1,276 @@
+/*
+ * 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
+ */
+/*
+ * @(#) balden@belcarra.com|otg/functions/network/otg-config.h|20060419204258|52982
+ *
+ */
+
+
+/*
+ * tristate " Network Function Driver"
+ */
+#define CONFIG_OTG_NETWORK
+
+
+/*
+ * hex "VendorID (hex value)"
+ * default "0x15ec"
+ */
+#define CONFIG_OTG_NETWORK_VENDORID
+
+/*
+ * hex "ProductID (hex value)"
+ * default "0xf001"
+ */
+#define CONFIG_OTG_NETWORK_PRODUCTID
+
+/*
+ * hex "bcdDevice (binary-coded decimal)"
+ * default "0x0100"
+ */
+#define CONFIG_OTG_NETWORK_BCDDEVICE
+
+/*
+ * string "iManufacturer (string)"
+ * default "Belcarra"
+ * This will be used as the iManufacturer string descriptor.
+ */
+#define CONFIG_OTG_NETWORK_MANUFACTURER
+
+/*
+ * string "iProduct (string)"
+ * default "Belcarra Network Device"
+ * This will be used as the iProduct string descriptor.
+ */
+#define CONFIG_OTG_NETWORK_PRODUCT_NAME
+
+/*
+ * bool 'Enable MDLM-BLAN networking'
+ * BLAN supports non-infrastructure devices in virtual
+ * bridged network environment.
+ */
+#define CONFIG_OTG_NETWORK_BLAN
+
+/*
+ * string " iConfiguration (string)"
+ * default "BLAN Net Cfg"
+ * This will be used as the Configuration string descriptor
+ * for the BLAN configuration.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_DESC
+
+/*
+ * string " iInterface (string)"
+ * default "Comm/Data Intf"
+ * This will be used as the Interface string descriptor
+ * for the BLAN configuration.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_INTF
+
+
+/*
+ * bool " append 32bit CRC"
+ * default TRUE
+ * Setting this allows the host and device to verify that
+ * all network frames have been successfully transferred.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_CRC
+
+/*
+ * bool " Automatically configure the network interface"
+ * default TRUE
+ * The driver will automatically configure the network interface
+ * based on the IPADDR sent from the host to the device during
+ * enumeration. This eliminates the need for hotplug.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_AUTO_CONFIG
+
+
+/*
+ * bool " Pad after"
+ * default FALSE
+ * This is used get around some buggy hardware, it
+ * is generally not used. Say No.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_PADAFTER
+
+/*
+ * bool " Pad before"
+ * default FALSE
+ * This is used get around some buggy hardware, it
+ * is generally not used. Say No.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_PADBEFORE
+
+/*
+ * int " Pad bytes"
+ * default "8"
+ * This is used get around some buggy hardware, it
+ * is generally not used. Leave at default.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_PADBYTES
+
+/*
+ * bool " Fermat"
+ * default FALSE
+ * This is used get around some buggy hardware, it
+ * is generally not used. Say No.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_FERMAT
+
+/*
+ * bool " Non-Bridged"
+ * default FALSE
+ * Tell host to not to enable bridge.
+ */
+#define CONFIG_OTG_NETWORK_BLAN_NONBRIDGED
+
+/*
+ * bool 'Enable MDLM-SAFE networking'
+ * SAFE supports infrastructure devices but does not
+ * require support for SET INTERFACE or interrupt endpoints
+ */
+#define CONFIG_OTG_NETWORK_SAFE
+
+/*
+ * string " SAFE Data Interface iConfiguration (string)"
+ * default "SAFE Net Cfg"
+ * This will be used as the Configuration string descriptor
+ * for the SAFE configuration.
+ */
+#define CONFIG_OTG_NETWORK_SAFE_DESC
+
+/*
+ * string " SAFE Data Interface iInterface (string)"
+ * default "Data Intf"
+ * This will be used as the Interface string descriptor
+ * for the SAFE configuration.
+ */
+#define CONFIG_OTG_NETWORK_SAFE_INTF
+
+/*
+ * bool " append 32bit CRC"
+ * default TRUE
+ * Setting this allows the host and device to verify that
+ * all network frames have been successfully transferred.
+ */
+#define CONFIG_OTG_NETWORK_SAFE_CRC
+
+/*
+ * bool " Pad before"
+ * default FALSE
+ * This is used get around some buggy hardware, it
+ * is generally not used. Say No.
+ */
+#define CONFIG_OTG_NETWORK_SAFE_PADBEFORE
+
+/*
+ * bool " Bridged"
+ * default FALSE
+ * Tell host to enable bridge.
+ */
+#define CONFIG_OTG_NETWORK_SAFE_BRIDGED
+
+/*
+ * bool 'Enable CDC networking'
+ * CDC implements the USB CDC Class Specification
+ * to support infra-structure devices
+ */
+#define CONFIG_OTG_NETWORK_CDC
+
+/*
+ * string " CDC iConfiguration (string)"
+ * default "SAFE Net Cfg"
+ * This will be used as the Configuration string descriptor
+ * for the CDC configuration.
+ */
+#define CONFIG_OTG_NETWORK_CDC_DESC
+
+/*
+ * string " CDC Comm Interface iInterface (string)"
+ * default "Comm Intf"
+ * This will be used as the Interface string descriptor
+ * for the CDC configuration.
+ */
+#define CONFIG_OTG_NETWORK_CDC_COMM_INTF
+
+/*
+ * string " CDC Data (diabled) iInterface (string)"
+ * default "Data (Disabled) Intf"
+ * This will be used as the Interface string descriptor
+ * for the CDC no DATA interface.
+ */
+#define CONFIG_OTG_NETWORK_CDC_NODATA_INTF
+
+/*
+ * string " CDC Data Interface iInterface (string)"
+ * default "Data Intf"
+ * This will be used as the Interface string descriptor
+ * for the CDC Data Interface.
+ */
+#define CONFIG_OTG_NETWORK_CDC_DATA_INTF
+
+
+/*
+ * bool 'Enable Basic network'
+ * Implement a very simple network configuration
+ * with a single data interface.
+ */
+#define CONFIG_OTG_NETWORK_BASIC
+
+/*
+ * string " BASIC Data Interface iConfiguration (string)"
+ * default "BASIC Net Cfg"
+ * This will be used as the Configuration string descriptor
+ * for the BLAN configuration.
+ */
+#define CONFIG_OTG_NETWORK_BASIC_DESC
+
+/*
+ * string " BASIC Data Interface iInterface (string)"
+ * default "Data Intf"
+ * This will be used as the Interface string descriptor
+ * for the BLAN data interface.
+ */
+#define CONFIG_OTG_NETWORK_BASIC_INTF
+
+/*
+ * bool 'Enable Basic2 network'
+ * Implement a very simple network configuration with
+ * two interfaces.
+ */
+#define CONFIG_OTG_NETWORK_BASIC2
+
+/*
+ * string " BASIC2 Data Interface iConfiguration (string)"
+ * default "BASIC Net Cfg"
+ * This will be used as the Configuration string descriptor
+ * for the BASIC2 configuration.
+ */
+#define CONFIG_OTG_NETWORK_BASIC2_DESC
+
+/*
+ * string " BASIC2 Comm Interface iInterface (string)"
+ * default "Comm Intf"
+ * This will be used as the Interface string descriptor
+ * for the BASIC2 configuration.
+ */
+#define CONFIG_OTG_NETWORK_BASIC2_COMM_INTF
+
+/*
+ * string " BASIC2 Data Interface iInterface (string)"
+ * default "Data Intf"
+ * This will be used as the Interface string descriptor
+ * for the BASIC2 configuration.
+ */
+#define CONFIG_OTG_NETWORK_BASIC2_DATA_INTF
diff --git a/drivers/otg/functions/network/safe-if.c b/drivers/otg/functions/network/safe-if.c
new file mode 100644
index 000000000000..b040bfe75ed9
--- /dev/null
+++ b/drivers/otg/functions/network/safe-if.c
@@ -0,0 +1,284 @@
+/*
+ * 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
+ */
+/*
+ * otg/functions/network/safe.c - Network Function Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/functions/network/safe-if.c|20070220211521|55659
+ *
+ * Copyright (c) 2002-2006 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Chris Lynne <cl@belcarra.com>
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*!
+ * @file otg/functions/network/safe-if.c
+ * @brief This file implements the required descriptors to implement
+ * a SAFE network device with a single interface.
+ *
+ * The SAFE network driver implements the SAFE protocol descriptors.
+ *
+ * The SAFE protocol is designed to support smart devices that want
+ * to create a virtual network between them host and other similiar
+ * devices.
+ *
+ * @ingroup NetworkFunction
+ */
+
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/usbp-func.h>
+
+#include "network.h"
+
+#define NTT network_fd_trace_tag
+
+#if defined(CONFIG_OTG_NETWORK_SAFE) || defined(_OTG_DOXYGEN)
+
+/* USB SAFE Configuration ******************************************************************** */
+
+/* @name Safe Ethernet Configuration
+ *
+ * @{
+ */
+
+static struct usbd_class_header_function_descriptor safe_class_1 = {
+ bFunctionLength:0x05,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_HEADER,
+ bcdCDC: __constant_cpu_to_le16(0x0110), //0x10, 0x01,
+};
+
+static struct usbd_class_mdlm_descriptor safe_class_2 = {
+ bFunctionLength: 0x15,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_MDLM,
+ bcdVersion: __constant_cpu_to_le16( 0x0100), //0x00, 0x01,
+ bGUID: {
+ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */
+ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */ },
+};
+
+static struct usbd_class_safe_descriptor safe_class_3 = {
+ bFunctionLength: 0x06,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_MDLMD,
+ bGuidDescriptorType: 0x00,
+ bmNetworkCapabilities: 0x00,
+ bmDataCapabilities: 0x00, /* bDetailData */
+};
+
+static struct usbd_class_ethernet_networking_descriptor safe_class_4 = {
+ bFunctionLength: 0x0d,
+ bDescriptorType: USB_DT_CLASS_SPECIFIC,
+ bDescriptorSubtype: USB_ST_ENF,
+ iMACAddress: 0x0,
+ bmEthernetStatistics: __constant_cpu_to_le32(0x00),
+ wMaxSegmentSize: __constant_cpu_to_le16(0x5ea),
+ wNumberMCFilters: __constant_cpu_to_le16(0x00),
+ bNumberPowerFilters: 0,
+};
+
+
+static struct usbd_generic_class_descriptor *safe_comm_class_descriptors[] = {
+ (struct usbd_generic_class_descriptor *) &safe_class_1,
+ (struct usbd_generic_class_descriptor *) &safe_class_2,
+ (struct usbd_generic_class_descriptor *) &safe_class_3,
+ (struct usbd_generic_class_descriptor *) &safe_class_4, };
+
+/*
+ * @}
+ */
+
+
+/*! Data Alternate Interface Description List
+ */
+static struct usbd_alternate_description safe_alternate_descriptions[] = {
+ {
+ .iInterface = "Belcarra USBLAN - MDLM/SAFE",
+ .bInterfaceClass = COMMUNICATIONS_INTERFACE_CLASS,
+ .bInterfaceSubClass = COMMUNICATIONS_MDLM_SUBCLASS,
+ .bInterfaceProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .classes = sizeof (safe_comm_class_descriptors) / sizeof (struct usbd_generic_class_descriptor *),
+ .class_list = safe_comm_class_descriptors,
+ .endpoints = ENDPOINTS,
+ .endpoint_index = net_fd_endpoint_index,
+ },
+};
+/* Interface descriptions and descriptors
+ */
+/*! Interface Description List
+ */
+static struct usbd_interface_description safe_interfaces[] = {
+ {
+ .alternates = sizeof (safe_alternate_descriptions) / sizeof (struct usbd_alternate_description),
+ .alternate_list = safe_alternate_descriptions,
+ },
+};
+
+
+/* ********************************************************************************************* */
+
+/*! net_fd_function_enable - enable the function driver
+ *
+ * Called for usbd_function_enable() from usbd_register_device()
+ *
+ * @param function_instance pointer to function instance
+ * @return int
+ */
+
+int safe_fd_function_enable (struct usbd_function_instance *function_instance)
+{
+ struct usbd_class_ethernet_networking_descriptor *ethernet = &safe_class_4;
+ //struct usbd_class_network_channel_descriptor *channel = &safe_class_5 ;
+
+#if 0
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17)
+ char address_str[14];
+ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#else
+ char address_str[20];
+ sprintf(address_str, "%02x%02x%02x%02x%02x%02x",
+ local_dev_addr[0], local_dev_addr[1], local_dev_addr[2],
+ local_dev_addr[3], local_dev_addr[4], local_dev_addr[5]);
+#endif
+#endif
+ //ethernet->iMACAddress = usbd_alloc_string(address_str);
+ //channel->iName = usbd_alloc_string(system_utsname.nodename);
+
+ return net_fd_function_enable(function_instance,
+ network_blan, net_fd_recv_urb_mdlm,
+ net_fd_start_xmit_mdlm,
+ net_fd_start_recv_mdlm, 0
+ );
+}
+
+
+/* ********************************************************************************************* */
+/*! safe_fd_function_ops - operations table for network function driver
+ */
+struct usbd_function_operations safe_fd_function_ops = {
+ .function_enable = safe_fd_function_enable,
+ .function_disable = net_fd_function_disable,
+
+ .device_request = net_fd_device_request,
+ .endpoint_cleared = net_fd_endpoint_cleared,
+
+ .set_configuration = net_fd_set_configuration,
+ .set_interface = NULL,
+ .reset = net_fd_reset,
+ .suspended = net_fd_suspended,
+ .resumed = net_fd_resumed,
+};
+
+/*! function driver description
+ */
+struct usbd_interface_driver safe_interface_driver = {
+ .driver = {
+ .name = "net-safe-if",
+ .fops = &safe_fd_function_ops, },
+ .interfaces = sizeof (safe_interfaces) / sizeof (struct usbd_interface_description),
+ .interface_list = safe_interfaces,
+ .endpointsRequested = ENDPOINTS,
+ .requestedEndpoints = net_fd_endpoint_requests,
+
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = COMMUNICATIONS_ENCM_SUBCLASS,
+ .bFunctionProtocol = COMMUNICATIONS_NO_PROTOCOL,
+ .iFunction = "Belcarra USBLAN - MDLM/SAFE",
+};
+
+/* ********************************************************************************************* */
+
+/*! safe_mod_ini
+ * @return interface driver register result code
+ */
+int safe_mod_init (void)
+{
+ struct usbd_class_ethernet_networking_descriptor *ethernet;
+ struct usbd_class_network_channel_descriptor *channel;
+
+ int len = 0;
+ char buf[255];
+
+ buf[0] = 0;
+
+#if 0
+ // Update the iMACAddress field in the ethernet descriptor
+ {
+ char address_str[14];
+ snprintf(address_str, 13, "%02x%02x%02x%02x%02x%02x",
+ remote_dev_addr[0], remote_dev_addr[1], remote_dev_addr[2],
+ remote_dev_addr[3], remote_dev_addr[4], remote_dev_addr[5]);
+ if ((ethernet = &safe_class_4)) {
+ if (ethernet->iMACAddress) {
+ usbd_free_string_descriptor(ethernet->iMACAddress);
+ }
+ ethernet->iMACAddress = usbd_alloc_string(function_instance, address_str);
+ }
+ }
+#endif
+
+#ifdef CONFIG_OTG_NETWORK_SAFE_PADBEFORE
+ safe_class_3.bmDataCapabilities |= BMDATA_PADBEFORE;
+ len += sprintf(buf + len, "PADBEFORE: %02x ", safe_class_3.bmDataCapabilities);
+#endif
+#ifdef CONFIG_OTG_NETWORK_SAFE_CRC
+ safe_class_3.bmDataCapabilities |= BMDATA_CRC;
+ len += sprintf(buf + len, "CRC: %02x ", safe_class_3.bmDataCapabilities);
+#endif
+#ifndef CONFIG_OTG_NETWORK_SAFE_BRIDGED
+ safe_class_3.bmNetworkCapabilities |= BMNETWORK_NONBRIDGED;
+ len += sprintf(buf + len, "NONBRIDGE: %02x ", safe_class_3.bmNetworkCapabilities);
+#endif
+#ifndef CONFIG_OTG_NETWORK_SAFE_AUTO_CONFIG
+ safe_class_3.bmNetworkCapabilities |= BMNETWORK_NO_VENDOR;
+ len += sprintf(buf + len, "NO VENDOR: %02x ", safe_class_3.bmNetworkCapabilities);
+#endif
+ if (strlen(buf))
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, buf);
+
+ return usbd_register_interface_function (&safe_interface_driver, "net-safe-if", NULL);
+}
+
+/*! safe_mod_exit
+ */
+void safe_mod_exit(void)
+{
+ usbd_deregister_interface_function (&safe_interface_driver);
+}
+
+#else /* CONFIG_OTG_NETWORK_SAFE */
+/*!
+ * @brief safe_mod_init
+ * @return none
+ */
+int safe_mod_init (void)
+{
+ return 0;
+}
+/*! safe_mod_exit
+ */
+void safe_mod_exit(void)
+{
+}
+#endif /* CONFIG_OTG_NETWORK_SAFE */
diff --git a/drivers/otg/hardware/Kconfig-imx31ads b/drivers/otg/hardware/Kconfig-imx31ads
new file mode 100644
index 000000000000..7ad69c8c967a
--- /dev/null
+++ b/drivers/otg/hardware/Kconfig-imx31ads
@@ -0,0 +1,71 @@
+#
+# @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/imx31ads/Kconfig-imx31ads|20070614183949|43876
+# Copyright (c) 2006 Belcarra Technologies 2005 Corp
+#
+
+# CONFIG_OTG_PLATFORM_OTG # Offer OTG Configuration
+
+# CONFIG_OTG_IMX31EVB
+
+# CONFIG_OTG_ISP1301_IMX31ADS # Compile ISP1301 IMX31ADS
+# CONFIG_OTG_MC13738_IMX31ADS # Compile MC13783 IMX31ADS
+
+# CONFIG_OTG_IMX31ADS # IMX31ADS
+# CONFIG_OTG_IMX31ADS # Use ISP1301
+# CONFIG_OTG_IMX31ADS # Use MC13783
+# CONFIG_OTG_IMX31ADS # Use MC13783
+# CONFIG_OTG_PLATFORM # Compile as OTG Dual-role Device
+# CONFIG_OTG_TRADITIONAL # Compile as Traditional USB Peripheral
+
+config OTG_IMX31ADS
+ tristate "Freescale iMX31ADS Evaluation Board"
+ depends on OTG && (MACH_MX31ADS)
+
+ ---help---
+ This implements On-The-Go USB Support for the iMX31ADS EVB.
+
+if ( OTG_IMX31ADS != 'n')
+
+ #config OTG_PLATFORM_OTG
+ # bool
+ # default OTG_IMX31ADS
+
+ config OTG_PLATFORM_USB
+ bool
+ default USB_ARC_OTG
+
+ config OTG_PLATFORM_USBD
+ bool
+ default !USB_ARC_OTG
+
+ config OTG_HIGH_SPEED_CAPABLE
+ bool
+ default OTG_IMX31ADS
+
+ #config OTG_REMOTE_WAKEUP_CAPABLE
+ # bool
+ # default OTG_IMX31ADS
+
+ #config OTG_BUS_POWERED_CAPABLE
+ # bool
+ # default OTG_IMX31ADS
+
+ config OTG_BTC_ARC
+ bool
+ default OTG_IMX31ADS
+
+
+ config OTG_ARC
+ bool
+ default USB_ARC
+
+ config OTG_ARC_OTGHS
+ bool
+ default USB_ARC_OTGHS
+
+ config OTG_ARC_OTGFS
+ bool
+ default USB_ARC_OTGFS
+
+
+endif
diff --git a/drivers/otg/hardware/Kconfig-isp1301 b/drivers/otg/hardware/Kconfig-isp1301
new file mode 100644
index 000000000000..6bd7a0720f61
--- /dev/null
+++ b/drivers/otg/hardware/Kconfig-isp1301
@@ -0,0 +1,15 @@
+
+
+# @(#) sl@belcarra.com|otg/platform/isp1301/Kconfig-isp1301|20060719063203|59940
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+#menu "Phillips ISP1301 Support"
+
+
+ config OTG_ISP1301_PROCFS
+ depends on OTG_ISP1301 && PROC_FS
+ bool "ISP1301 Proc FS debug"
+ ---help---
+ Compile debug support, implements /proc/isp1301 which
+ will display all of the ISP1301 registers.
+
+#endmenu
diff --git a/drivers/otg/hardware/Kconfig-zasevb b/drivers/otg/hardware/Kconfig-zasevb
new file mode 100644
index 000000000000..9bf8da75077e
--- /dev/null
+++ b/drivers/otg/hardware/Kconfig-zasevb
@@ -0,0 +1,221 @@
+#
+# @(#) sp/root@belcarra.com/debian-black.(none)|otg/platform/zasevb/Kconfig-zasevb|20070829214932|07457
+# Copyright (c) 2005 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+#
+
+# CONFIG_OTG_PLATFORM_OTG # Offer OTG Configuration
+
+# CONFIG_OTG_ZASEVB # Make
+# CONFIG_OTG_ISP1301 # Make ISP1301 driver
+
+# CONFIG_OTG_ISP1301_ZASEVB # Compile ISP1301 ZASEVB
+# CONFIG_OTG_MC13738_ZASEVB # Compile MC13783 ZASEVB
+
+# CONFIG_OTG_ZASEVB # ZASEVB
+# CONFIG_OTG_ZASEVB_ISP1301 # Use ISP1301
+# CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY # Use MC13783
+# CONFIG_OTG_ZASEVB_MC13783_POWERIC # Use MC13783
+# CONFIG_OTG_PLATFORM # Compile as OTG Dual-role Device
+# CONFIG_OTG_TRADITIONAL # Compile as Traditional USB Peripheral
+
+config OTG_ZASEVB
+ tristate "Freescale ZAS Evaluation Board using TDI FS USB"
+ depends on OTG && (MACH_MXC27530EVB || MACH_MXC30030EVB || MACH_MXC30030ADS || \
+ MACH_MXC91131EVB || MACH_I30030ADS || MACH_I30030EVB)
+
+ ---help---
+ This implements On-The-Go USB Support for the ZAS EVB using the TDI Full Speed USB Device Controller.
+
+choice
+ prompt "Select OTG Transceiver"
+ depends on OTG_ZASEVB
+ config OTG_ZASEVB_ISP1301
+ bool 'Use the BaseBoard ISP1301 (S2.1 on S2.2 on)'
+ depends on OTG_ZASEVB !=n
+ ---help---
+ This will use the ISP1301 on the EVB Base Board.
+ Switch 2-1 and switch 2-2 should both be in the ON position.
+
+ SW2.1 ON
+ SW2.2 ON
+
+
+ config OTG_ZASEVB_MC13783_LEGACY
+ bool 'Use the MC13783 Transceiver (legacy API)'
+ depends on OTG_ZASEVB && (MACH_MXC30030ADS || MACH_MXC30030EVB || MACH_I30030ADS || \
+ MACH_I30030EVB || MACH_MXC27530EVB) && (MXC_MC13783_CONNECTIVITY || MC13783_CONNECTIVITY) && \
+ (!(MXC_MC13783_PMIC || MXC_PMIC_MC13783))
+ ---help---
+ This will use the Freescale MC13783 and the Freescale Connectivity driver.
+ For BrassBoard works without any special settings.
+
+ For ZAS EVB with MC13783 daughter card, Switch 2-1 should be on, Switch 2-2
+ should be off.
+ If you are using MC13783 daughter card with MXC91231 or MXC275-30 EVB, you
+ should set S6.1: OFF and S6.2: ON.
+ If you are using MC13783 daughter card with MXC91321 or MXC91331 or i.300-30 EVB
+ or MXC300-30 EVB, you should set S6.1: ON and S6.2: OFF.
+
+
+ config OTG_ZASEVB_MC13783_PMIC
+ bool 'Use the MC13783 Transceiver (PMIC API)'
+ depends on OTG_ZASEVB && (MACH_MXC30030ADS || MACH_MXC30030EVB || MACH_I30030ADS || \
+ MACH_I30030EVB || MACH_MXC27530EVB) && (MXC_MC13783_CONNECTIVITY || MC13783_CONNECTIVITY ) && \
+ (MXC_MC13783_PMIC || MXC_PMIC_MC13783)
+ ---help---
+ This will use the Freescale MC13783 and the Freescale Connectivity driver.
+ For BrassBoard works without any special settings.
+
+ For ZAS EVB with MC13783 daughter card, Switch 2-1 should be on, Switch 2-2
+ should be off.
+ If you are using MC13783 daughter card with MXC91231 or MXC275-30 EVB, you
+ should set S6.1: OFF and S6.2: ON.
+ If you are using MC13783 daughter card with MXC91321 or MXC91331 or i.300-30 EVB
+ or MXC300-30 EVB, you should set S6.1: ON and S6.2: OFF.
+
+endchoice
+choice
+ prompt "Select OTG Transceiver Configuration"
+ depends on OTG && OTG_ZASEVB
+
+ config OTG_ZASEVB_DIFFERENTIAL_UNIDIRECTIONAL
+ bool 'Differential Unidirectional (6 wire)'
+ depends on (ARCH_MXC91331 || ARCH_MXC91321)
+ ---help---
+ Both the USBOTG HWMODE and Transceiver need to be configured
+ for the data transceiver connection.
+
+ This selects a Differential unidirectional 6 wire connection.
+
+ On MC13783 daughter board
+ SW4.1 = OFF
+ SW4.2 = OFF
+ SW4.3 = OFF
+ SW4.4 = ON
+
+
+ config OTG_ZASEVB_DIFFERENTIAL_BIDIRECTIONAL
+ bool 'Differential Bidirectional (4 wire)'
+ depends on ARCH_MXC91231 || ARCH_MXC91131
+ ---help---
+ Both the USBOTG HWMODE and Transceiver need to be configured
+ for the data transceiver connection.
+
+ This selects a Differential bidirectional 4 wire connection.
+
+ config OTG_ZASEVB_SINGLE_ENDED_UNIDIRECTIONAL
+ bool 'Singled Ended Unidirectional (6 wire)'
+ depends on (ARCH_MXC91331 || ARCH_MXC91321)
+ ---help---
+ Both the USBOTG HWMODE and Transceiver need to be configured
+ for the data transceiver connection.
+
+ This selects a Single-Ended unidirectional 6 wire connection.
+
+ config OTG_ZASEVB_SINGLE_ENDED_BIDIRECTIONAL
+ bool 'Singled Ended Bidirectional (3 wire)'
+ depends on ARCH_MXC91231 || ARCH_MXC91131
+ ---help---
+ Both the USBOTG HWMODE and Transceiver need to be configured
+ for the data transceiver connection.
+
+ On MC13783 daughter board
+ SW4.1 = OFF
+ SW4.2 = OFF or ON (different from data sheet !!)
+ SW4.3 = OFF
+ SW4.4 = ON
+
+ This selects a Single-Ended bidirectional 3 wire connection.
+
+endchoice
+choice
+ prompt "Select Timer"
+ depends on OTG && OTG_ZASEVB
+ ---help---
+ Select OTG timer according to platform structure
+
+ config OTG_HRT
+# depends on HIGH_RES_TIMERS
+ bool 'High Resolution Timer'
+ ---help---
+ Using high resolution timer, if present and enabled (preferred)
+
+ config OTG_TIMER_NONE
+ bool 'No OTG Timer -- certain features will not work properly'
+ ---help---
+ Select this option to have no timer source available to OTG
+
+# config OTG_GPTR
+# depends on (HIGH_RES_TIMERS=n)
+# bool 'General Purpose Timer'
+# ---help---
+# Using the shared or non-shared general purpose timer
+# Warning: this option is DEPRECATED. Please enable HIGH_RES_TIMERS
+
+endchoice
+
+if ( OTG_ZASEVB != 'n')
+
+ config OTG_PLATFORM_OTG
+ bool
+ default OTG_ZASEVB
+
+ config OTG_PLATFORM_USB
+ bool
+ default OTG_ZASEVB
+
+ config OTG_PLATFORM_USBD
+ bool
+ default OTG_ZASEVB
+
+ #config OTG_HIGH_SPEED_CAPABLE
+ # bool
+ # default OTG_ZASEVB
+
+ config OTG_REMOTE_WAKEUP_CAPABLE
+ bool
+ default OTG_ZASEVB
+
+ config OTG_BUS_POWERED_CAPABLE
+ bool
+ default OTG_ZASEVB
+
+ config OTG_FREESCALE_TDI
+ bool
+ default OTG_ZASEVB
+
+ config OTG_ISP1301
+ bool
+ depends on OTG_ZASEVB_ISP1301
+ default OTG_ZASEVB
+
+ config OTG_ZASEVB_MC13783_CONNECTIVITY
+ bool
+ depends on OTG_ZASEVB_MC13783_LEGACY
+ default OTG_ZASEVB
+
+ config OTG_ZASEVB_MC13783_POWERIC
+ bool
+ depends on OTG_ZASEVB_MC13783_LEGACY
+ default OTG_ZASEVB
+
+ # config OTG_MXC_SELF_POWERED
+ # bool 'Enable if device is Self-Powered'
+ # default y
+
+ #if ( OTG_MXC_SELF_POWERED = 'n')
+ # config OTG_MXC_SELF_BMAXPOWER
+ # int 'bMaxPower - 2mA units'
+ # default 1
+ # ---help---
+ # The amount of power the device will draw in 2mA units.
+ # Must be non-zero even for self powered device.
+ #endif
+
+ # config OTG_MXC_REMOTE_WAKEUP
+ # bool 'Enable if you want device to implement Remote Wakeup'
+ # default y
+
+
+endif
diff --git a/drivers/otg/hardware/Kconfig-zasevb-arc b/drivers/otg/hardware/Kconfig-zasevb-arc
new file mode 100644
index 000000000000..71aac5367d8c
--- /dev/null
+++ b/drivers/otg/hardware/Kconfig-zasevb-arc
@@ -0,0 +1,51 @@
+#
+# @(#) sp@belcarra.com|otg/platform/zasevb/Kconfig-zasevb-arc|20070629180415|22301
+# Copyright (c) 2005 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+#
+
+# CONFIG_OTG_PLATFORM_OTG # Offer OTG Configuration
+
+# CONFIG_OTG_ZASEVB_ARC # Make
+
+# CONFIG_OTG_PLATFORM # Compile as OTG Dual-role Device
+# CONFIG_OTG_TRADITIONAL # Compile as Traditional USB Peripheral
+
+config OTG_ZASEVB_ARC
+ tristate "Freescale ZAS Evaluation Board using ARC HS USB"
+ depends on OTG && ((MACH_MXC30020EVB) || (MACH_MXC30031ADS))
+
+ ---help---
+ This implements On-The-Go USB Support for the ZAS EVB using the ARC High Speed USB Device Controller.
+
+if ( OTG_ZASEVB_ARC != 'n')
+
+ config OTG_PLATFORM_OTG
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_PLATFORM_USB
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_PLATFORM_USBD
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_HIGH_SPEED_CAPABLE
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_REMOTE_WAKEUP_CAPABLE
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_BUS_POWERED_CAPABLE
+ bool
+ default OTG_ZASEVB_ARC
+
+ config OTG_BTC_ARC
+ bool
+ default OTG_ZASEVB_ARC
+
+endif
diff --git a/drivers/otg/hardware/Makefile b/drivers/otg/hardware/Makefile
new file mode 100644
index 000000000000..58453d7b6efc
--- /dev/null
+++ b/drivers/otg/hardware/Makefile
@@ -0,0 +1,14 @@
+#
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/freescale/Makefile-l26|20070525074749|26894
+# Copyright (c) 2006-2007 Belcarra Technologies 2005 Corp
+
+
+ifdef CONFIG_OTG_ZASEVB
+include drivers/otg/hardware/Makefile-zasevb-l26
+endif
+ifdef CONFIG_OTG_ZASEVB_ARC
+include drivers/otg/hardware/Makefile-zasevb-arc-l26
+endif
+ifdef CONFIG_OTG_IMX31ADS
+include drivers/otg/hardware/Makefile-imx31ads-l26
+endif
diff --git a/drivers/otg/hardware/Makefile-imx31ads-l26 b/drivers/otg/hardware/Makefile-imx31ads-l26
new file mode 100644
index 000000000000..cc35de2e7434
--- /dev/null
+++ b/drivers/otg/hardware/Makefile-imx31ads-l26
@@ -0,0 +1,35 @@
+#
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/imx31ads/Makefile-imx31ads-l26|20070615010012|14157
+# Copyright (c) 2005 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+#OTG=$(TOPDIR)/drivers/otg
+#OTGCORE_DIR=$(OTG)/otgcore
+#USBCORE=$(TOPDIR)/drivers/usb
+#EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+#EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+
+
+OTG=$(TOPDIR)/drivers/otg
+EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+
+
+# #########################################################################################################
+
+#
+# USB Configurations - peripheral, host or both
+#
+# Produce imx31ads module.
+#
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL),y)
+obj-$(CONFIG_OTG_IMX31ADS) += imx31ads_usb.o
+endif
+
+
+ximx31ads_usb-objs := imx31ads-l26.o otg-dev.o pcd.o arc-dev.o \
+ l26-ocd-dev.o l26-ocd.o arc-ocd.o arc-tcd.o arc-pcd.o
+
+imx31ads_usb-objs := imx31ads-l26.o otg-dev.o pcd.o arc-dev.o \
+ l26-ocd-dev.o l26-ocd.o arc-pcd.o
diff --git a/drivers/otg/hardware/Makefile-zasevb-arc-l26 b/drivers/otg/hardware/Makefile-zasevb-arc-l26
new file mode 100644
index 000000000000..d27f85df5f7c
--- /dev/null
+++ b/drivers/otg/hardware/Makefile-zasevb-arc-l26
@@ -0,0 +1,32 @@
+#
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/zasevb/Makefile-zasevb-arc-l26|20070615010012|07869
+# Copyright (c) 2005 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+#EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+#EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+
+
+OTG=$(TOPDIR)/drivers/otg
+EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+
+
+# #########################################################################################################
+
+#
+# USB Configurations - peripheral, host or both
+#
+# Produce zasevb module.
+#
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL),y)
+obj-$(CONFIG_OTG_ZASEVB_ARC) += zasevb_usb.o
+endif
+
+
+ozasevb_usb-objs := zasevb-arc-l26.o otg-dev.o pcd.o arc-dev.o \
+ l26-ocd-dev.o l26-ocd.o arc-tcd.o arc-pcd.o arc-ocd.o
+
+zasevb_usb-objs := zasevb-arc-l26.o otg-dev.o pcd.o arc-dev.o \
+ l26-ocd-dev.o l26-ocd.o arc-pcd.o
diff --git a/drivers/otg/hardware/Makefile-zasevb-l26 b/drivers/otg/hardware/Makefile-zasevb-l26
new file mode 100644
index 000000000000..d6989e4294aa
--- /dev/null
+++ b/drivers/otg/hardware/Makefile-zasevb-l26
@@ -0,0 +1,131 @@
+#
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/zasevb/Makefile-zasevb-l26|20061222212434|28091
+# Copyright (c) 2005 Belcarra
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+
+OTG=$(TOPDIR)/drivers/otg
+OTGCORE_DIR=$(OTG)/otgcore
+USBCORE=$(TOPDIR)/drivers/usb
+MXCATLASDIR=$(TOPDIR)/drivers/mxc/mc13783_legacy
+EXTRA_CFLAGS += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+EXTRA_CFLAGS_nostdinc += -I$(OTG) -Wno-unused -Wno-format -I$(USBCORE) -I$(OTGCORE_DIR) -I$(MXCATLASDIR)
+
+# #########################################################################################################
+
+#
+# USB Configurations - peripheral, host or both
+#
+# Produce zasevb_usb module.
+#
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL),y)
+obj-$(CONFIG_OTG_ZASEVB) += zasevb_usb.o
+endif
+
+ifeq ($(CONFIG_OTG_USB_HOST),y)
+obj-$(CONFIG_OTG_ZASEVB) += zasevb_usb.o
+endif
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL_OR_HOST),y)
+obj-$(CONFIG_OTG_ZASEVB) += zasevb_usb.o
+endif
+
+zasevb_usb-objs := zasevb-l26.o
+zasevb_usb-objs += mxc-ocd.o
+zasevb_usb-objs += mxc-procfs.o
+
+
+ifdef CONFIG_ARCH_MXC91321
+zasevb_usb-objs += mxc91321-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91331
+zasevb_usb-objs += mxc91331-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91231
+zasevb_usb-objs += mxc91231-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91131
+zasevb_usb-objs += mxc91131-gpio.o
+endif
+
+
+
+zasevb_usb-$(CONFIG_OTG_USB_PERIPHERAL) += pcd.o mxc-pcd.o
+zasevb_usb-$(CONFIG_OTG_USB_HOST) += pcd.o mxc-pcd.o
+zasevb_usb-$(CONFIG_OTG_USB_HOST) += mxc-hcd.o mxc-l26.o
+zasevb_usb-$(CONFIG_OTG_USB_PERIPHERAL_OR_HOST) += pcd.o mxc-pcd.o
+zasevb_usb-$(CONFIG_OTG_USB_PERIPHERAL_OR_HOST) += mxc-hcd.o mxc-l26.o
+
+#zasevb_usb-$(CONFIG_OTG_GPTR) += mxc-gptcr.o l26-ocd.o
+#zasevb_usb-$(CONFIG_OTG_HRT) += mxc-hrt.o l26-ocd.o
+zasevb_usb-$(CONFIG_OTG_GPTR) += mxc-gptcr.o
+zasevb_usb-$(CONFIG_OTG_HRT) += mxc-hrt.o
+
+zasevb_usb-${CONFIG_OTG_ZASEVB_ISP1301} += zasevb-isp1301.o isp1301.o \
+ isp1301-procfs.o i2c-l26.o
+
+zasevb_usb-${CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY} += zasevb-mc13783.o mc13783.o
+zasevb_usb-$(CONFIG_OTG_ZASEVB_MC13783_PMIC) += zasevb-pmic.o mxc-pmic.o
+zasevb_usb-${CONFIG_ZASEVB_MC13783_POWERIC} += zasevb-poweric.o
+zasevb_usb-$(CONFIG_OTG_ZASEVB_PMIC) += zasevb-sc55112.o sc55112.o
+
+# #########################################################################################################
+
+#
+# OTG Configurations - peripheral with SRP or full OTG Device
+#
+# Produce zasevb_otg module.
+#
+
+ifeq ($(CONFIG_OTG_BDEVICE_WITH_SRP),y)
+obj-$(CONFIG_OTG_ZASEVB) += zasevb_otg.o
+endif
+
+ifeq ($(CONFIG_OTG_DEVICE),y)
+obj-$(CONFIG_OTG_ZASEVB) += zasevb_otg.o
+endif
+
+zasevb_otg-objs := zasevb-l26.o
+zasevb_otg-objs += mxc-ocd.o
+zasevb_otg-objs += mxc-procfs.o
+
+
+ifdef CONFIG_ARCH_MXC91321
+zasevb_otg-objs += mxc91321-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91331
+zasevb_otg-objs += mxc91331-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91231
+zasevb_otg-objs += mxc91231-gpio.o
+endif
+
+ifdef CONFIG_ARCH_MXC91131
+zasevb_otg-objs += mxc91131-gpio.o
+endif
+
+
+
+zasevb_otg-$(CONFIG_OTG_BDEVICE_WITH_SRP) += pcd.o mxc-pcd.o
+zasevb_otg-$(CONFIG_OTG_DEVICE) += pcd.o mxc-pcd.o
+
+zasevb_otg-$(CONFIG_OTG_DEVICE) += mxc-hcd.o mxc-l26.o
+#zasevb_otg-$(CONFIG_OTG_GPTR) += mxc-gptcr.o l26-ocd.o
+#zasevb_otg-$(CONFIG_OTG_HRT) += mxc-hrt.o l26-ocd.o
+zasevb_otg-$(CONFIG_OTG_GPTR) += mxc-gptcr.o
+zasevb_otg-$(CONFIG_OTG_HRT) += mxc-hrt.o
+
+
+zasevb_otg-${CONFIG_OTG_ZASEVB_ISP1301} += zasevb-isp1301.o isp1301.o \
+ isp1301-procfs.o i2c-l26.o
+
+
+zasevb_otg-${CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY} += zasevb-mc13783.o mc13783.o
+zasevb_otg-$(CONFIG_OTG_ZASEVB_MC13783_PMIC) += zasevb-pmic.o mxc-pmic.o
+zasevb_otg-${CONFIG_ZASEVB_MC13783_POWERIC} += zasevb-poweric.o
+zasevb_otg-$(CONFIG_OTG_ZASEVB_PMIC) += zasevb-sc55112.o sc55112.o
diff --git a/drivers/otg/hardware/arc-dev.c b/drivers/otg/hardware/arc-dev.c
new file mode 100644
index 000000000000..c6070e1783ed
--- /dev/null
+++ b/drivers/otg/hardware/arc-dev.c
@@ -0,0 +1,291 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/arc-dev.c - MX31 Device driver
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/arc/arc-dev.c|20070921192720|26657
+ *
+ * Copyright (c) 2006-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Shahrad Payandeh Lynne <sp@lbelcarra.com>,
+ * Stuart Lynne <sl@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/arc-dev.c
+ * @brief Belcarra Freescale ARC Device driver.
+ *
+ * @ingroup ARC
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+#include <otg/otg-api.h>
+#include <otg/otg-dev.h>
+
+#include <otg/otg-utils.h>
+
+#include <asm/arch/gpio.h>
+#include <asm/memory.h>
+#include <asm/io.h>
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+
+//#include <asm/arch/arc_otg.h>
+
+#define MX31_USB_NAME "arc_udc"
+
+static const char driver_name[] = MX31_USB_NAME;
+
+
+/* ********************************************************************************************* */
+/* arc Proc FS
+ */
+
+#define SHOW(s, r, c) seq_printf(s, "%20s [%8x] %08x %s\n", #r, &r, r, c)
+#define BITS(s, c) seq_printf(s, "%24s: ", c);
+#define BIT(s, r, m) seq_printf(s, "%s%s ", #m, (r & m) ? "" : "/");
+#define NL(s) seq_printf(s, "\n");
+
+/*! arc_dev_show - called to display information
+ * @param s
+ * @param unused
+ */
+static int arc_dev_show(struct seq_file *s, void *unused)
+{
+ int etdn;
+ unsigned long flags;
+ u32 u;
+
+ seq_printf(s, "MX31 USB\n\n");
+
+ seq_printf(s, "\nOTG Registers\n");
+
+ //SHOW(s, UOG_ID, "Host ID");
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+/*! arc_dev_open - open a file
+ * @param inode
+ * @param file
+ */
+
+static int arc_dev_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, arc_dev_show, PDE(inode)->data);
+}
+
+/*! struct file_operatons arc_dev_proc_ops */
+static struct file_operations arc_dev_proc_ops = {
+ .open = arc_dev_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const char proc_filename[] = "arcusb";
+
+/* ********************************************************************************************* */
+/*!
+ * arc_udc_isr() - architecture isr wrapper
+ * @param irq
+ * @param data
+ * @param r
+ */
+irqreturn_t arc_udc_isr(int irq, void *data)
+{
+
+// printk(KERN_INFO"%s: --\n", __FUNCTION__);
+ return otg_dev_isr(irq,data);
+}
+
+
+/* ********************************************************************************************* */
+/*! arc_dev_remove - called to remove
+ * @param device - device instance
+ * @param otg_device_driver - otg driver instance
+ */
+static int __init_or_module
+arc_dev_remove(struct device *device, struct otg_device_driver *otg_device_driver)
+{
+ struct otg_dev *otg_dev = dev_get_drvdata(device);
+
+ otg_dev_remove(device, otg_device_driver);
+
+ return 0;
+}
+
+
+static void arc_dev_release(struct device *dev)
+{
+}
+
+/*! arc_dev_probe - called to initialize
+ * @param device - device
+ * @param otg_device_driver - otg driver instance
+ */
+static int
+arc_dev_probe(struct device *device, struct otg_device_driver *otg_device_driver)
+{
+ struct platform_device *platform_device = to_platform_device(device);
+ int probe = -1;
+
+
+ /* verify correct driver name
+ */
+ THROW_IF(strcmp(platform_device->name, driver_name), error);
+
+ device->release = arc_dev_release;
+
+
+
+ /* do generic device probe, this will allocate resources (e.g. irqs)
+ * and call the sub-driver probe functions.
+ */
+ THROW_IF((probe = otg_dev_probe(device, otg_device_driver, NULL)), error);
+ printk(KERN_INFO"%s: device: %p\n", __FUNCTION__, platform_device);
+
+ return 0;
+
+ CATCH(error) {
+ return -ENODEV;
+ }
+}
+
+/* ********************************************************************************************* */
+/*! Linux 2.6 Device Driver Support
+ *
+ * The Linux 2.6 Core requires that host driver be registered as a device driver. These
+ * implement the requirements for the Linux Device Driver support.
+ *
+ */
+
+/* ********************************************************************************************* */
+/*! arc_device_suspend - called to suspend udc
+ * @param device - device
+ * @param state -
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+arc_device_suspend(struct device *device, pm_message_t state)
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+arc_device_suspend(struct device *device, u32 state, u32 phase)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return -EINVAL;
+}
+
+/*! arc_device_resume - called to resume udc
+ * @param device - device
+ */
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+arc_device_resume(struct device *device)
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+arc_device_resume(struct device *device, u32 phase)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return -EINVAL;
+}
+
+/* ********************************************************************************************* */
+/*! arc_dev_driver - define a device_driver structure for this driver
+ */
+static struct device_driver arc_dev_driver = {
+
+ .name = MX31_USB_NAME,
+ .bus = &platform_bus_type,
+
+ .suspend = arc_device_suspend,
+ .resume = arc_device_resume,
+
+};
+
+/* ********************************************************************************************* */
+/*! Module init/exit
+ *
+ * Register this a linux device driver and platform. This provides the necessary framework
+ * expected by the Linux 2.6 USB Core.
+ *
+ */
+
+/*! arc_dev_module_init
+ * @param otg_device_driver - platform device driver structure
+ * @param device_probe - platform probe function
+ * @param device_remove - platform rmove function
+ */
+int arc_dev_module_init(struct otg_device_driver *otg_device_driver,
+ int (*device_probe)(struct device *),
+ int (*device_remove)(struct device *))
+{
+ int driver_registered = -1;
+ struct proc_dir_entry *pde = NULL;
+
+ printk(KERN_INFO"%s: platform: %s driver: %s\n",
+ __FUNCTION__, otg_device_driver->name, arc_dev_driver.name) ;
+
+ /* Setup the otg_device_driver probe function to point back to this one,
+ * and the actual driver probe to the provided function. This allows the
+ * platform driver to provide the overall device structure.
+ *
+ * We also provide a wrapper ISR in case we need to do something before
+ * or after the otg_dev_isr() function has run.
+ */
+ THROW_UNLESS(otg_device_driver, error);
+ THROW_UNLESS(device_probe, error);
+ arc_dev_driver.probe = device_probe;
+ arc_dev_driver.remove = device_remove;
+ otg_device_driver->probe = arc_dev_probe;
+ otg_device_driver->remove = arc_dev_remove;
+ otg_device_driver->isr = arc_udc_isr;
+
+ /* register the arc-dev drivers
+ */
+ THROW_IF((driver_registered = driver_register(&arc_dev_driver)), error);
+ THROW_UNLESS((pde = create_proc_entry(proc_filename, 0, NULL)), error);
+ pde->proc_fops = &arc_dev_proc_ops;
+ return 0;
+ CATCH(error) {
+ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
+ if (pde) remove_proc_entry(proc_filename, NULL);
+ UNLESS (driver_registered) driver_unregister(&arc_dev_driver);
+ return -EINVAL;
+ }
+}
+
+/*! arc_dev_module_exit
+ */
+void arc_dev_module_exit(struct otg_device_driver *otg_device_driver)
+{
+ otg_device_driver->probe = NULL;
+ otg_device_driver->isr = NULL;
+ remove_proc_entry(proc_filename, NULL);
+ driver_unregister(&arc_dev_driver);
+}
diff --git a/drivers/otg/hardware/arc-hardware.h b/drivers/otg/hardware/arc-hardware.h
new file mode 100644
index 000000000000..452e89f71f09
--- /dev/null
+++ b/drivers/otg/hardware/arc-hardware.h
@@ -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
+ */
+/*
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public 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 otg/hardware/arc_hardware.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 ARC_EP0 0
+#define ARC_DIR_OUT 0
+#define ARC_DIR_IN 1
+
+
+/* ### 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[63];
+ 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 */
+} __attribute__ ((packed));
+
+/* 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
+
+/* 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)
+
+/* 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_RSV (0x00000001)
+#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)
+
+/*!
+ * 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
+ */
+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_addr_t td_dma;
+ struct ep_td_struct *next_td_virt;
+
+ /*!
+ * make it an even 8 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)
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * memory kmalloc with alignning size and zero the content
+ * @base :output parameter. It store the base address before align.
+ * Return value it the address after align
+ *
+ */
+static void *KMALLOC_ALIGN(size_t size, int flags, unsigned int align,
+ void **base)
+{
+/* #define MY_ALIGN(n) ((n)+(-(n) & (align - 1))) */
+
+ *base = kmalloc(size + align, flags);
+ if (*base == NULL)
+ return NULL;
+ memset(*base, 0, (size + align));
+ return (void *)ALIGN((unsigned int)(*base), align);
+}
+
+/* Bulk only class request */
+#define USB_BULK_RESET_REQUEST 0xff
+
+
+typedef enum arc_belcarra_transceiver_event {
+ arc_vbus_valid,
+ arc_vbus_invalid,
+} arc_belcarra_transceiver_event_t;
+
+
+
+
+#endif /* __ARCOTG_UDC_H */
diff --git a/drivers/otg/hardware/arc-ocd.c b/drivers/otg/hardware/arc-ocd.c
new file mode 100644
index 000000000000..d2f9b5261425
--- /dev/null
+++ b/drivers/otg/hardware/arc-ocd.c
@@ -0,0 +1,158 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/arc-ocd.c -- Generic Linux 2.6 Timer
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/arc/arc-ocd.c|20070612232808|00655
+ *
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Shahrad Payandeh Lynne <sp@lbelcarra.com>,
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/arc-ocd.c
+ * @brief Belcarra Freescale ARC Device driver.
+ *
+ * Provides ticks via gettimeofday() and optional a OTG timer
+ * using high resolution timer.
+ *
+ * CONFIG_HIGH_RES_TIMERS=y
+ *
+ * Optionally provides accurate OTG timer using Linux High
+ * Resolution Timer.
+ *
+ * CONFIG_HIGH_RES_TIMERS=n
+ *
+ * Start timer is a dummy if High Resolution Timers not
+ * enabled.
+ *
+ * @ingroup ARC
+ * @ingroup OCD
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/pcd-include.h>
+#include <otg/otg-dev.h>
+
+#include "arc-hardware.h"
+
+//#warning NEED CONDITIONAL for imx-regs.h
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/time.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+
+
+#define MXC_GPT_GPTCNT (IO_ADDRESS(GPT1_BASE_ADDR + 0x24))
+
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * arc_ocd_start_timer() - start a timer for otg state machine
+ * Set or reset timer to interrupt in number of uS (micro-seconds).
+ *
+ * @param otg
+ * @param usec
+ */
+int arc_ocd_start_timer(struct otg_instance *otg, int usec)
+{
+ otg_event(otg, TMOUT, otg->ocd->TAG, "l26 Timer - FAKE TMOUT");
+ return 0;
+}
+
+/* arc_ocd_ticks - get current ticks
+ */
+otg_tick_t arc_ocd_ticks (void)
+{
+ unsigned long ticks = 0;
+
+ ticks = __raw_readl(MXC_GPT_GPTCNT);
+
+ return (otg_tick_t) ticks;
+}
+
+/* arc_ocd_elapsed - return micro-seconds between two tick values
+ */
+otg_tick_t arc_ocd_elapsed(otg_tick_t *t1, otg_tick_t *t2)
+{
+ otg_tick_t ticks = (((*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1)));
+ //return (otg_tick_t) ticks / 16; // 381 vs 367
+ //return (otg_tick_t) (ticks * 2) / 31; // 201 vs 188
+ return (otg_tick_t) ticks / 17; // 185 vs 189
+}
+
+
+struct ocd_ops arc_ocd_ops = {
+ .start_timer = arc_ocd_start_timer,
+ .ticks = arc_ocd_ticks,
+ .elapsed = arc_ocd_elapsed,
+};
+
+
+static void
+arc_ocd_dev_remove(struct otg_dev *otg_dev)
+{
+}
+
+static int
+arc_ocd_dev_probe(struct otg_dev *otg_dev)
+{
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+static struct otg_dev_driver arc_ocd_driver = {
+ .name = "arc_udc",
+ .id = OTG_DRIVER_OCD,
+
+ .probe = arc_ocd_dev_probe,
+ .remove = arc_ocd_dev_remove,
+
+ .ops = &arc_ocd_ops,
+};
+
+/* ********************************************************************************************* */
+
+/*! arc_ocd_module_init() - module init
+ */
+int arc_ocd_module_init (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return otg_dev_register_driver(otg_device_driver, &arc_ocd_driver);
+}
+
+/*!
+ * arc_ocd_module_exit() - module exit
+ */
+void arc_ocd_module_exit (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ otg_dev_unregister_driver (otg_device_driver, &arc_ocd_driver);
+}
diff --git a/drivers/otg/hardware/arc-pcd.c b/drivers/otg/hardware/arc-pcd.c
new file mode 100644
index 000000000000..03e37695d7ee
--- /dev/null
+++ b/drivers/otg/hardware/arc-pcd.c
@@ -0,0 +1,1088 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/arc-pcd.c -- Freescale HS (ARC) USBOTG Peripheral Controller driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/arc/arc-pcd.c|20070918212334|07623
+ *
+ * Copyright (c) 2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Shahrad Payandeh Lynne <sp@lbelcarra.com>,
+ * Stuart Lynne <sl@lbelcarra.com>,
+ */
+/*!
+ * @file otg/hardware/arc-pcd.c
+ * @brief USB Peripheral Controller Driver
+ * This implements the Freescale HS (ARC) USBOTG Peripheral Controller Driver.
+ *
+ * @ingroup ARC
+ * @ingroup PCD
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+#include <otg/pcd-include.h>
+#include <otg/otg-dev.h>
+#include <linux/dma-mapping.h>
+#include <asm/arch/arc_otg.h>
+#include <linux/dmapool.h>
+#include <linux/delay.h>
+#include "arc-hardware.h"
+#include "arc.h"
+
+#define HAVE_PLATFORM_ARC_REMOTE_WAKEUP 1
+
+#define TRACE_VERBOSE 1
+#define UDC_MAX_ENDPOINTS 16
+#define UDC_NAME "arc"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+
+/* ep_qh_base store the base address before 2K align */
+static struct ep_queue_head *ep_qh_base;
+static struct arcotg_udc *udc_controller;
+extern void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on);
+
+/*! arc_read_setup_buffer
+ */
+static void arc_read_setup_buffer(struct pcd_instance *pcd, u8 * buffer_ptr)
+{
+ struct ep_queue_head *qh = &udc_controller->ep_qh[0 * 2 + ARC_DIR_OUT];
+ int timeout;
+
+ consistent_sync(qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE);
+
+ /* C.f 39.16.3.2.1 Setup Phase - Setup Packet Handling (2.3 hardware and later)
+ */
+
+ /* 1. Clear ENDPTSETUPSTAT */
+ UOG_ENDPTSETUPSTAT |= (1 << 0); /* Clear recieve endpoint status */
+
+ do {
+ /* 2. set tripwire */
+ UOG_USBCMD |= USB_CMD_SUTW;
+
+ /* 3. read setup buffer */
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+
+ /* 4. check tripwire, loop if reset */
+ } while (!(UOG_USBCMD & USB_CMD_SUTW));
+
+ /* 5. reset tripwire */
+ UOG_USBCMD &= ~USB_CMD_SUTW;
+
+ // XXX This needs to be fixed, should not need to resort to timeout
+ timeout = 10000000;
+ while ((UOG_ENDPTSETUPSTAT & 1) && --timeout) { continue; }
+ if (timeout == 0) printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+}
+
+/*! arc_read_rcv_buffer
+ *
+ * Recover number of bytes DMA'd to receive buffer, sync.
+ */
+int arc_read_rcv_buffer (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ struct arc_private_struct *privdata = endpoint->privdata;
+ struct ep_td_struct *curr_td = privdata->cur_dtd;
+ struct ep_queue_head *qh = privdata->cur_dqh;
+ struct usbd_urb *rx_urb = endpoint->rcv_urb;
+
+ /* sync qh and td structures, note that urb-buffer was invalidated in arc_add_buffer_to_dtd() */
+ consistent_sync(qh, sizeof(struct ep_queue_head), DMA_FROM_DEVICE);
+ consistent_sync(curr_td, sizeof(struct ep_td_struct), DMA_FROM_DEVICE);
+
+ if (rx_urb) {
+ int length = rx_urb->buffer_length -
+ ((le32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
+
+ if (TRACE_VERBOSE) TRACE_MSG4(pcd->TAG, "buffer_length: %d alloc_length: %d Len: %d (%x)" ,
+ rx_urb->buffer_length, rx_urb->alloc_length, length,
+ le32_to_cpu(curr_td->size_ioc_sts));
+ return length;
+ }
+ TRACE_MSG1(pcd->TAG, "NO RCV URB (%x)" , le32_to_cpu(curr_td->size_ioc_sts));
+ return 0;
+}
+
+/*! arc_udc_init
+ */
+void arc_udc_release (void)
+{
+ kfree(ep_qh_base);
+ ep_qh_base = NULL;
+ if (udc_controller) {
+ if (udc_controller->ep_dtd) LKFREE(udc_controller->ep_dtd);
+ LKFREE(udc_controller);
+ udc_controller = NULL;
+ }
+}
+
+/*! arc_udc_init
+ */
+static struct arcotg_udc *arc_udc_init (struct otg_dev *otg_dev)
+{
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct platform_device *pdev = to_platform_device(device);
+
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ struct otg_instance *otg = otg_dev->otg_instance;
+ struct pcd_instance *pcd = otg_dev->pcd_instance;
+ struct arcotg_udc *udc = NULL;
+ int timeout;
+
+
+ /* Setting up the udc structure */
+ THROW_UNLESS((udc = (struct arcotg_udc *) CKMALLOC(sizeof(struct arcotg_udc))), error);
+
+ /* Allocate queue */
+ THROW_UNLESS((udc->ep_qh = (struct ep_queue_head *) KMALLOC_ALIGN( USB_MAX_PIPES * sizeof(struct ep_queue_head),
+ GFP_KERNEL | GFP_DMA, 2 * 1024, (void **)&ep_qh_base)), error);
+ THROW_UNLESS(ep_qh_base, error);
+ THROW_UNLESS((udc->ep_dtd = (struct ep_td_struct *) CKMALLOC(USB_MAX_PIPES * sizeof(struct ep_td_struct))), error);
+
+ /* Stop, reset and wait for the UDC to reset */
+ UOG_USBCMD &= ~USB_CMD_RUN_STOP;
+ UOG_USBCMD |= USB_CMD_CTRL_RESET;
+
+ timeout = 10000000; // XXX This needs to be fixed, should not need to resort to timeout
+ while ((UOG_USBCMD & USB_CMD_CTRL_RESET) && --timeout) { continue; }
+ if (timeout == 0) {
+ printk(KERN_DEBUG "%s: TIMEOUT\n", __FUNCTION__);
+ return NULL;
+ }
+
+ /* Setup UDC mode and disable lock out mode*/
+ UOG_USBMODE |= USB_MODE_CTRL_MODE_DEVICE | USB_MODE_SETUP_LOCK_OFF;
+
+ UOG_EPLISTADDR = virt_to_phys(udc->ep_qh);
+ UOG_EPLISTADDR &= USB_EP_LIST_ADDRESS_MASK;
+
+ /* Setup transceiver type, N.B. this must be done in one assignment */
+ UOG_PORTSC1 = (UOG_PORTSC1 & ~PORTSCX_PHY_TYPE_SEL) | pdata->xcvr_type;
+
+ #if !defined(CONFIG_OTG_HIGH_SPEED)
+ UOG_PORTSC1 |= (0x01000000);
+ #endif
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ CATCH(error) {
+ if (ep_qh_base) kfree(ep_qh_base);
+ if (udc) {
+ if (udc->ep_dtd) LKFREE(udc->ep_dtd);
+ LKFREE(udc);
+ }
+ udc = NULL;
+ }
+ return udc;
+}
+
+/*! arc_udc_run
+ */
+static int arc_udc_run (void)
+{
+ /* enable the interrupt sources we want */
+ UOG_USBINTR |= 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;
+
+ /* Set to peripheral mode */
+ UOG_USBMODE |= USB_MODE_CTRL_MODE_DEVICE;
+
+ /* set interrupt threshold to zero, run udc */
+ UOG_USBCMD &= ~USB_CMD_ITC;
+ UOG_USBCMD |= USB_CMD_ITC_NO_THRESHOLD | USB_CMD_RUN_STOP;
+ return 0;
+}
+
+/*! arc_udc_stop
+ */
+void arc_udc_stop (void)
+{
+ /* Disable all interrupts */
+ UOG_USBINTR = 0;
+ UOG_USBCMD &= ~USB_CMD_RUN_STOP;
+}
+
+/*! arc_pcd_start
+ */
+void arc_pcd_start (struct pcd_instance *pcd)
+{
+ u32 id = (u32) UOG_ID;
+ TRACE_MSG1(pcd->TAG, "Initialize UDC and start id: %08x", id);
+ arc_udc_run ();
+}
+
+/*! arc_pcd_stop
+ */
+void arc_pcd_stop(struct pcd_instance *pcd)
+{
+ TRACE_MSG0(pcd->TAG, "Stop");
+ arc_udc_stop ();
+}
+
+/*! arc_pcd_disable
+ */
+void arc_pcd_disable(struct pcd_instance *pcd)
+{
+ TRACE_MSG0(pcd->TAG, "Disabled");
+}
+
+/*! arc_pcd_framenum() - get current framenum
+ */
+static u16 arc_pcd_framenum (struct pcd_instance *pcd)
+{
+ u16 frame = (UOG_FRINDEX & USB_FRINDEX_MASKS);
+ frame &= ~(0x7);
+ frame >>= 3;
+ return (int)( frame );
+}
+
+/* arc_pcd_ticks - get current ticks
+ */
+#define MXC_GPT_GPTCNT (IO_ADDRESS(GPT1_BASE_ADDR + 0x24))
+otg_tick_t arc_pcd_ticks (struct pcd_instance *pcd)
+{
+ unsigned long ticks = 0;
+ ticks = __raw_readl(MXC_GPT_GPTCNT);
+ return (otg_tick_t) ticks;
+}
+
+/* arc_pcd_elapsed - return micro-seconds between two tick values
+ */
+otg_tick_t arc_pcd_elapsed(otg_tick_t *t1, otg_tick_t *t2)
+{
+ otg_tick_t ticks = (((*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1)));
+ return (otg_tick_t) ticks / 17; // 185 vs 189
+}
+
+/*****************************************************************************************/
+/*! arc_add_buffer_to_dtd
+ *
+ * C.f. 39.16.5.3 - case 1: Link list is empty
+ */
+static void arc_add_buffer_to_dtd (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint,
+ int dir, int len, int offset)
+{
+ struct otg_instance *otg = pcd->otg;
+ struct usbd_urb *urb = endpoint->active_urb;
+ struct arc_private_struct *privdata = endpoint->privdata;
+
+ u8 hs = pcd->bus->high_speed;
+ u8 physicalEndpoint = endpoint->physicalEndpoint[hs];
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+ u16 wMaxPacketSize = endpoint->wMaxPacketSize[hs];
+ struct ep_queue_head *dQH = &udc_controller->ep_qh[2 * epnum + dir];
+ struct ep_td_struct *dtd = &(udc_controller->ep_dtd[2 * epnum + dir]);
+
+ u32 mask = 0;
+ int timeout1 = 0;
+ int timeout2 = 0;
+
+ u32 endptstat = -1;
+ u32 endptprime = -1;
+ u32 endptcomplete = -1;
+
+ TRACE_MSG6(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x STATUS: %08x %s",
+ 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE,
+ (u32)dQH->size_ioc_int_sts, (dir == ARC_DIR_OUT) ? "OUT" : "IN");
+
+ if (urb && urb->buffer) {
+
+ TRACE_MSG4(pcd->TAG, "buffer: %x length: %d alloc: %d dir: %d ",
+ urb->buffer, urb->actual_length, urb->buffer_length, dir);
+
+ /* flush cache for IN */
+ if ((dir == ARC_DIR_IN) && urb->actual_length)
+ consistent_sync(urb->buffer, urb->actual_length, DMA_TO_DEVICE);
+
+ /* invalidate cache for OUT */
+ else if ((dir == ARC_DIR_OUT) && urb->buffer_length)
+ consistent_sync(urb->buffer, urb->alloc_length, DMA_FROM_DEVICE);
+ }
+
+ /* Set size and interrupt on each dtd, Clear reserved field,
+ * set pointers and flush from cache, and save in cur_dqh for dtd_releases()
+ */
+ memset(dtd, 0, sizeof(struct ep_td_struct));
+ dtd->size_ioc_sts = cpu_to_le32(((len << DTD_LENGTH_BIT_POS) | DTD_IOC | DTD_STATUS_ACTIVE));
+ dtd->size_ioc_sts &= cpu_to_le32(~DTD_RESERVED_FIELDS);
+ dtd->buff_ptr0 = cpu_to_le32(endpoint->active_urb ? (u32) (virt_to_phys (endpoint->active_urb->buffer + offset)) : 0);
+ dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+ dtd->next_td_virt = NULL;
+ consistent_sync(dtd, sizeof(struct ep_td_struct), DMA_TO_DEVICE);
+ privdata->cur_dqh = dQH;
+
+ /* Case 1 - Step 1 - Write dQH next pointer and dQH terminate bit to 0 as single DWord */
+ dQH->next_dtd_ptr = cpu_to_le32( virt_to_phys((void *)dtd) & EP_QUEUE_HEAD_NEXT_POINTER_MASK);
+
+ /* Case 1 - Step 2 - Clear active and halt bit */
+ dQH->size_ioc_int_sts &= le32_to_cpu(~(EP_QUEUE_HEAD_STATUS_ACTIVE | EP_QUEUE_HEAD_STATUS_HALT));
+
+ consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_TO_DEVICE);
+
+ /* Case 1 - Step 3 - Prime endpoint by writing ENDPTPRIME */
+ mask = (dir == ARC_DIR_OUT) ? (1 << epnum) : (1 << (epnum + 16));
+
+ /* Verify that endpoint PRIME is not set, wait if necessary. */
+ for (timeout1 = 0; (UOG_ENDPTPRIME & mask) && (timeout1 ++ < 100); udelay(1));
+
+ /* ep0 needs extra tests */
+ UNLESS(epnum) {
+ /* C.f. 39.16.3.2.2 Data Phase */
+ UOG_ENDPTPRIME |= mask;
+ for (timeout2 = 0; timeout2++ < 100; ) {
+ endptprime = UOG_ENDPTPRIME; // order may be important
+ endptstat = UOG_ENDPTSTAT; // we check stat after prime
+ BREAK_IF(endptstat & mask);
+ BREAK_UNLESS(endptprime & mask);
+ }
+ if (!(endptstat & mask) && !(endptprime & mask)) {
+ TRACE_MSG2(pcd->TAG, "[%2d] ENDPTSETUPSTAT: %04x PREMATURE FAILUURE", 2*epnum+dir, UOG_ENDPTSETUPSTAT);
+ }
+ TRACE_MSG6(pcd->TAG, "[%2d] ENDPTPRIME %08x ENPTSTAT: %08x mask: %08x timeout: %d:%d SET",
+ 2*epnum+dir, UOG_ENDPTPRIME, UOG_ENDPTSTAT, mask, timeout1, timeout2);;
+ }
+ /* epn general case */
+ else {
+ /* Hit PRIME bit until STATUS bit is set. */
+ UOG_ENDPTPRIME |= mask;
+ //for (timeout2 = 0; !(UOG_ENDPTSTAT & mask) && (timeout2++ < 100); /* UOG_ENDPTPRIME |= mask */);
+
+ for (timeout2 = 0; timeout2++ < 100; ) {
+ endptprime = UOG_ENDPTPRIME; // order may be important
+ endptstat = UOG_ENDPTSTAT; // we check stat after prime
+ endptcomplete = UOG_ENDPTCOMPLETE;
+ BREAK_IF(endptstat & mask);
+ BREAK_IF(endptcomplete & mask);
+ UNLESS(endptprime & mask) {
+ TRACE_MSG8(pcd->TAG, "[%2d] ENDPTPRIME %08x:%08x ENPTSTAT: %08x:%08x COMPLETE: %08x "
+ "mask: %08x timeout: %x NOT SET",
+ 2*epnum+dir, endptprime, UOG_ENDPTPRIME, endptstat, UOG_ENDPTSTAT,
+ UOG_ENDPTCOMPLETE,
+ mask, (timeout1 << 8) | timeout2);;
+ udelay(1);
+ endptstat = UOG_ENDPTSTAT; // we check stat after prime
+ BREAK_IF(endptstat & mask);
+ UOG_ENDPTPRIME |= mask;
+ }
+ }
+ TRACE_MSG8(pcd->TAG, "[%2d] ENDPTPRIME %08x:%08x ENPTSTAT: %08x:%08x COMPLETE: %08x "
+ "mask: %08x timeout: %x SET",
+ 2*epnum+dir, endptprime, UOG_ENDPTPRIME, endptstat, UOG_ENDPTSTAT,
+ UOG_ENDPTCOMPLETE,
+ mask, (timeout1 << 8) | timeout2);;
+ }
+
+}
+
+/* arc_dtd_releases
+ */
+static void arc_dtd_releases (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int dir)
+{
+ struct arc_private_struct *privdata = endpoint->privdata;
+ consistent_sync(privdata->cur_dtd, sizeof(struct ep_td_struct), (dir == ARC_DIR_OUT) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ consistent_sync(privdata->cur_dqh, sizeof(struct ep_queue_head), (dir == ARC_DIR_OUT) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+}
+
+
+/* arc_pcd_start_endpoint_in - start transmit
+ */
+void arc_pcd_start_endpoint_in (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ struct otg_instance *otg = pcd->otg;
+ struct usbd_urb *tx_urb;
+ u8 hs = pcd->bus->high_speed;
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+
+ UNLESS(epnum) {
+ TRACE_MSG1(pcd->TAG, "[ 0] ENDPTSETUPSTAT: %04x", UOG_ENDPTSETUPSTAT);
+ RETURN_IF (UOG_ENDPTSETUPSTAT & 0x1);
+ }
+ if ((tx_urb = endpoint->tx_urb)) {
+ endpoint->last = pcd_tx_sendzlp(endpoint) ? 0 : tx_urb->actual_length;
+ TRACE_MSG4(pcd->TAG, "[%2d] urb length: %d sent: %d %s",
+ epnum, tx_urb->actual_length, endpoint->sent, endpoint->last ? "" : "ZLP");
+ arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, endpoint->last, endpoint->sent);
+ }
+}
+
+/* arc_pcd_start_endpoint_out - start receive
+ */
+void arc_pcd_start_endpoint_out (struct pcd_instance *pcd,struct usbd_endpoint_instance *endpoint)
+{
+ //struct usbd_urb *rcv_urb;
+ //u8 hs = pcd->bus->high_speed;
+ //u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ //u8 epnum = bEndpointAddress & 0x3f;
+
+ struct usbd_urb *rcv_urb;
+ u8 hs;
+ u8 bEndpointAddress;
+ u8 epnum;
+
+ UNLESS (pcd) printk(KERN_INFO"%s: pcd: %p\n", __FUNCTION__, pcd);
+ UNLESS (pcd) printk(KERN_INFO"%s: pcd->bus: %p\n", __FUNCTION__, pcd->bus);
+ hs = pcd->bus->high_speed;
+
+ UNLESS (endpoint) printk(KERN_INFO"%s: endpoint: %p\n", __FUNCTION__, endpoint);
+ bEndpointAddress = endpoint->bEndpointAddress[hs];
+ epnum = bEndpointAddress & 0x3f;
+
+ UNLESS(epnum) {
+ //otg_led(LED2, 0);
+ TRACE_MSG1(pcd->TAG, "[ 0] ENDPTSETUPSTAT: %04x", UOG_ENDPTSETUPSTAT);
+ RETURN_IF (UOG_ENDPTSETUPSTAT & 0x1);
+ //otg_led(LED2, 1);
+ }
+ if ((rcv_urb = pcd_rcv_next_irq(endpoint))) {
+ TRACE_MSG3(pcd->TAG, "[%2d] urb length: %d actual: %d length: %d", epnum,
+ rcv_urb->actual_length, rcv_urb->buffer_length);
+ arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_OUT, rcv_urb->buffer_length, 0);
+ }
+}
+
+/*! arc_pcd_setup_ep - setup endpoint
+ * @param pcd -
+ * @param ep -
+ * @param endpoint
+ * @return none
+ */
+void arc_pcd_setup_ep (struct pcd_instance *pcd, unsigned int ep, struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_bus_instance *bus = pcd->bus;
+ struct ep_queue_head *dQH = NULL;
+
+ u8 hs = pcd->bus->high_speed;
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 bmAttributes = endpoint->bmAttributes[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+ u16 wMaxPacketSize = endpoint->wMaxPacketSize[hs];
+ u8 dir = (bEndpointAddress & 0x80) ? 1 : 0;
+ u8 type = bmAttributes & 0x3;
+
+ int timeout;
+ unsigned int tmp = 0;
+
+ struct arc_private_struct *privdata;
+
+ dQH = &udc_controller->ep_qh[2 * epnum + dir];
+ UNLESS(endpoint->privdata) {
+ endpoint->privdata = (struct arc_private_struct *) CKMALLOC (sizeof(struct arc_private_struct));
+ }
+ RETURN_UNLESS(privdata = endpoint->privdata);
+
+ /* Set the packet size */
+ switch (type) {
+ case 0: /* Control */
+ tmp = (wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS;
+ break;
+
+ case 2: /* Bulk */
+ case 3: /* Interrupt */
+ tmp = wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ break;
+ }
+ dQH->max_pkt_length = cpu_to_le32(tmp);
+
+ consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_TO_DEVICE);
+
+ /* Set the control register of endpoint */
+ tmp = dir ?
+ (EPCTRL_TX_ENABLE | ((u32)(type) << EPCTRL_TX_EP_TYPE_SHIFT) | (epnum ? EPCTRL_TX_DATA_TOGGLE_RST : 0)) :
+ (EPCTRL_RX_ENABLE | ((u32)(type) << EPCTRL_RX_EP_TYPE_SHIFT) | (epnum ? EPCTRL_RX_DATA_TOGGLE_RST : 0));
+
+ USBOTG_REG32(0x1c0 + epnum*4) |= tmp;
+
+ /* wait for correct writing */
+ timeout = 10000000; // XXX This needs to be fixed, should not need to resort to timeout
+ while ( ( !(USBOTG_REG32(0x1c0 + epnum*4) & (tmp & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)) ) ) && --timeout) { continue; }
+ if (timeout == 0)
+ printk(KERN_INFO "%s: TIMEOUT\n", __FUNCTION__);
+
+ TRACE_MSG4(pcd->TAG, "[%2x] Control register for size: %d is %x (%x)",
+ 2*epnum+dir, wMaxPacketSize, tmp, USBOTG_REG32(0x1c0 + epnum*4));
+
+ if (!epnum) {
+ dir = 1;
+ dQH = &udc_controller->ep_qh[2 * epnum + dir];
+ tmp = (wMaxPacketSize << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS;
+ dQH->max_pkt_length = le32_to_cpu(tmp);
+ consistent_sync(dQH, sizeof(struct ep_queue_head), DMA_FROM_DEVICE);
+ tmp = USBOTG_REG32(0x1c0 + epnum*4);
+ tmp |= EPCTRL_TX_ENABLE;
+ tmp |= ((unsigned int)(type) << EPCTRL_TX_EP_TYPE_SHIFT);
+ USBOTG_REG32(0x1c0 + epnum*4) = tmp;
+
+ // XXX This needs to be fixed, should not need to resort to timeout, wait for correct writing
+ timeout = 10000000;
+ while ( ( !(USBOTG_REG32(0x1c0 + epnum*4) & (tmp & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE)) ) ) && --timeout)
+ continue;
+ if (timeout == 0) printk(KERN_INFO "%s: TIMEOUT\n", __FUNCTION__);
+ }
+ privdata->cur_dtd = &(udc_controller->ep_dtd[2 * epnum + dir]);
+ privdata->cur_dqh = &udc_controller->ep_qh[2 * epnum + dir];
+}
+
+/* arc_pcd_disable_ep -
+ */
+void arc_pcd_disable_ep(struct pcd_instance *pcd, unsigned int ep, struct usbd_endpoint_instance *endpoint)
+{
+ TRACE_MSG1(pcd->TAG, "endpoint->privdata: %p", endpoint->privdata);
+ RETURN_UNLESS (endpoint->privdata);
+ LKFREE(endpoint->privdata);
+ endpoint->privdata = NULL;
+}
+
+
+/*! arc_pcd_cancel_irq
+ */
+void arc_pcd_cancel_irq (struct pcd_instance *pcd, struct usbd_urb *urb)
+{
+ struct usbd_endpoint_instance *endpoint = urb->endpoint;
+
+ u8 hs = pcd->bus->high_speed;
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+ u8 dir = (bEndpointAddress & 0x80) ? 1 : 0;
+
+ u32 mask = (dir == ARC_DIR_OUT) ? (1 << epnum) : (1 << (epnum + 16));
+
+ TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH",
+ 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask);
+
+
+ UOG_ENDPTFLUSH = mask;
+
+ TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH",
+ 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask);
+
+ TRACE_MSG5(pcd->TAG, "[%2d] USBCMD: %08x ENDPTPRIME: %08x COMPLETE: %08x mask: %08x FLUSH",
+ 2*epnum+dir, UOG_USBCMD, UOG_ENDPTPRIME, UOG_ENDPTCOMPLETE, mask);
+
+}
+
+
+extern void fsl_platform_test_mode_select(struct fsl_usb2_platform_data *pdata, int on);
+/* arc_pcd_device_feature - device feature
+ */
+int arc_pcd_device_feature(struct pcd_instance *pcd, int selector, int flag)
+{
+ u32 tmp = UOG_PORTSC1 | (selector << 16);
+ UOG_PORTSC1 = tmp;
+ return 0;
+}
+
+/*
+ * arc_endpoint_halted() - is endpoint halted
+ */
+static int
+arc_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ u8 hs = pcd->bus->high_speed;
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+ u8 dir = (bEndpointAddress & 0x80) ? 1 : 0;
+ u32 tmp = USBOTG_REG32(0x1c0 + epnum*4);
+ switch (dir) {
+ case USB_DIR_IN:
+ TRACE_MSG3(pcd->TAG, "epn: %d dir: %d ENDPTCTRLn: %04x USB_DIR_IN", epnum, dir, tmp);
+ return BOOLEAN( tmp & EPCTRL_TX_EP_STALL );
+
+ case USB_DIR_OUT:
+ TRACE_MSG3(pcd->TAG, "epn: %d dir: %d ENDPTCTRLn: %04x USB_DIR_OUT", epnum, dir, tmp);
+ return BOOLEAN( tmp & EPCTRL_RX_EP_STALL );
+ }
+ return 0;
+}
+
+/* arc_halt_endpoint - halt endpoint
+ */
+int arc_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag)
+{
+ u8 hs = pcd->bus->high_speed;
+ u8 bEndpointAddress = endpoint->bEndpointAddress[hs];
+ u8 epnum = bEndpointAddress & 0x3f;
+ u8 dir = (bEndpointAddress & 0x80) ? 1 : 0;
+ u32 tmp = USBOTG_REG32(0x1c0 + epnum*4);
+ u32 mask = (dir == USB_DIR_IN) ? EPCTRL_TX_EP_STALL : EPCTRL_RX_EP_STALL;
+
+ tmp = flag ? (tmp | mask) : (tmp & ~mask);
+ USBOTG_REG32(0x1c0 + epnum*4) = tmp;
+
+ TRACE_MSG5(pcd->TAG, "epn: %d dir: %d flag: %d ENDPTCTRLn: %04x USB_DIR_%s STALLING",
+ epnum, dir, flag, tmp, (dir == USB_DIR_IN) ? "IN" : "OUT");
+ return 0;
+}
+
+
+#if HAVE_PLATFORM_ARC_REMOTE_WAKEUP
+extern void fsl_platform_perform_remote_wakeup(struct fsl_usb2_platform_data *pdata);
+
+/*! arc_remote_wakeup
+ */
+static int arc_remote_wakeup(struct pcd_instance *pcd)
+{
+ struct usbd_bus_instance *bus = pcd->bus;
+ struct otg_dev *otg_dev = pcd->privdata;
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct platform_device *pdev = to_platform_device(device);
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ fsl_platform_perform_remote_wakeup(pdata);
+ return 0;
+}
+#endif
+
+/*! arc_vbus_status() - enable
+ * This is called to return status of the PCD and USBD stack.
+ * Start or stop the UDC.
+ */
+static int
+arc_vbus_status (struct pcd_instance *pcd)
+{
+ u32 vbus = UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS;
+ TRACE_MSG2(pcd->TAG, "UOG_PORTSC1: %x, vbus: %x", UOG_PORTSC1, vbus);
+ return BOOLEAN(vbus);
+}
+
+/*! arc_softcon
+ * @param pcd - pcd pointer
+ * @param flag -
+ *
+ * Enable or disable pullup.
+ */
+int arc_softcon(struct pcd_instance *pcd, int flag)
+{
+ TRACE_MSG1(pcd->TAG, "ARC PULLUP %s", flag ? "SET": "RESET");
+ UOG_USBCMD = flag ? (UOG_USBCMD | USB_CMD_RUN_STOP) : (UOG_USBCMD & ~USB_CMD_RUN_STOP);
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+
+struct usbd_pcd_ops usbd_pcd_ops = {
+ .bmAttributes =
+ #ifdef CONFIG_OTG_SELF_POWERED
+ USB_BMATTRIBUTE_SELF_POWERED |
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ USB_BMATTRIBUTE_REMOTE_WAKEUP |
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ //USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED |
+ USB_BMATTRIBUTE_RESERVED,
+ #ifndef CONFIG_OTG_SELF_POWERED
+ .bMaxPower = CONFIG_OTG_BMAXPOWER,
+ #else /* CONFIG_OTG_SELF_POWERED */
+ .bMaxPower = 1,
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ .max_endpoints = 0,
+ .high_speed_capable = TRUE,
+ .ep0_packetsize = 64,
+ .name = UDC_NAME,
+ .start = arc_pcd_start,
+ .stop = arc_pcd_stop,
+ .disable = arc_pcd_disable,
+ .disable_ep = arc_pcd_disable_ep,
+ .start_endpoint_in = arc_pcd_start_endpoint_in,
+ .start_endpoint_out = arc_pcd_start_endpoint_out,
+ .request_endpoints = pcd_request_endpoints,
+ .setup_ep = arc_pcd_setup_ep,
+ .cancel_in_irq = arc_pcd_cancel_irq,
+ .cancel_out_irq = arc_pcd_cancel_irq,
+ .device_feature = arc_pcd_device_feature,
+ .halt_endpoint = arc_halt_endpoint,
+ .endpoint_halted = arc_endpoint_halted,
+ .remote_wakeup = arc_remote_wakeup,
+ .vbus_status = arc_vbus_status,
+ .softcon = arc_softcon,
+ .ticks = arc_pcd_ticks,
+ .elapsed = arc_pcd_elapsed,
+ .framenum = arc_pcd_framenum,
+};
+
+/* ********************************************************************************************* */
+/*! arc_ep0_irq
+ */
+static irqreturn_t arc_ep0_irq(struct pcd_instance *pcd)
+{
+ struct usbd_endpoint_instance *endpoint = pcd->bus->endpoint_array + 0;
+ struct usbd_urb *urb = endpoint->active_urb;
+
+ /* SETUP - Device request received */
+ if (UOG_ENDPTSETUPSTAT & 0x1) {
+ struct usbd_device_request request;
+ TRACE_MSG2(pcd->TAG, "EP0 SETUP active: %p %x", urb, urb ? urb->flags : 0);
+
+ /* Complete any outstanding transfers */
+ if (urb && (urb->flags & USBD_URB_IN)) pcd_tx_complete_irq(endpoint, 0);
+ if (urb && (urb->flags & USBD_URB_OUT)) pcd_rcv_complete_irq(endpoint, 0, 0);
+
+ /* Read setup - reset UOG_ENDPTSETUPSTAT */
+ arc_read_setup_buffer(pcd, (u8 *) &request);
+
+ if (pcd_recv_setup_irq(pcd, &request)) {
+ /* endpoint requested not handled - need to stall */
+ TRACE_MSG0(pcd->TAG, "REQUEST NOT HANDLED - STALLED");
+ USBOTG_REG32(0x1c0 + 0*4) |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ return IRQ_HANDLED;
+ }
+ /* Finish if no data phase */
+ UNLESS (request.wLength) {
+ TRACE_MSG0(pcd->TAG, "REQUEST HANDLED OK - NO DATA PHASE");
+ arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, 0, 0);
+ }
+ return IRQ_HANDLED;
+ }
+ /* TX/IN Complete */
+ if (UOG_ENDPTCOMPLETE & 0x10000) {
+ TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE active: %p %x", urb, urb ? urb->flags : 0);
+
+ /* set address here if previous request was SET_ADDRESS */
+ if (pcd->new_address) {
+ UOG_DEVICEADDR = pcd->new_address << USB_DEVICE_ADDRESS_BIT_POS;
+ pcd->new_address = 0;
+ }
+
+ /* sync and clear complete bit */
+ arc_dtd_releases (pcd, endpoint, ARC_DIR_IN);
+ UOG_ENDPTCOMPLETE = 0x10000;
+
+ /* finish urb, reset zlp flag if it's there, we always send zlp */
+ if (pcd_tx_complete_irq(endpoint, 0)) {
+ TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE ZLP check: %p %x", urb, urb ? urb->flags : 0);
+ if (pcd_tx_sendzlp(endpoint)) {
+ pcd_tx_complete_irq(endpoint, 0);
+ TRACE_MSG2(pcd->TAG, "EP0 IN COMPLETE ZLP active: %p %x", urb, urb ? urb->flags : 0);
+ }
+ }
+ arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_OUT, 0, 0);
+ return IRQ_HANDLED;
+ }
+ /* RX/OUT Complete */
+ if (UOG_ENDPTCOMPLETE & 0x1) {
+ struct arc_private_struct *privdata = endpoint->privdata;
+ struct ep_td_struct *curr_td = privdata->cur_dtd;
+ int length;
+
+ /* sync and clear complete bit */
+ arc_dtd_releases (pcd, endpoint, ARC_DIR_OUT);
+ length = ((le32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
+ UOG_ENDPTCOMPLETE = 0x1;
+
+ TRACE_MSG3(pcd->TAG, "EP0 OUT COMPLETE active: %p %x length: %d", urb, urb ? urb->flags : 0, length);
+ UNLESS (urb && (urb->flags & USBD_URB_OUT)) {
+ TRACE_MSG0(pcd->TAG, "EP0 OUT COMPLETE - ZLP received");
+ return IRQ_HANDLED;
+ }
+ length = arc_read_rcv_buffer (pcd, endpoint);
+ pcd_rcv_complete_irq(endpoint, length, 0);
+ arc_add_buffer_to_dtd (pcd, endpoint, ARC_DIR_IN, 0, 0);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*! arc_epn_irq
+ */
+static irqreturn_t arc_epn_irq(struct pcd_instance *pcd)
+{
+ u32 endptcomplete;
+ /* N.B. need to loop until nothing left to do, endpoints can complete while
+ * working on other endpoints.
+ */
+ while ((endptcomplete = (UOG_ENDPTCOMPLETE & 0xfffefffe))) {
+
+ /* We have endptcomplete bits, immediately reset so that subsequent
+ * completions will be seen when we loop. Note that we mask ep0 status.
+ */
+ UOG_ENDPTCOMPLETE = endptcomplete;
+
+ /* Loop while non-zero - N.B. clz requires pre-test as it is undefined on zero */
+ while (endptcomplete) {
+ int bit = 31 - __builtin_clz(endptcomplete); /* highest non-zero bit */
+ int ep = bit & 0xf;
+ int dir = (bit > 15) ? ARC_DIR_IN : ARC_DIR_OUT;
+ struct usbd_endpoint_instance *endpoint = pcd->bus->endpoint_array + (2 * ep + dir);
+ struct arc_private_struct *privdata = endpoint->privdata;
+ struct ep_td_struct *cur_dtd = privdata->cur_dtd;
+ int errors = le32_to_cpu(cur_dtd->size_ioc_sts) & DTD_ERROR_MASK;
+ int remain_len;
+ TRACE_MSG6(pcd->TAG, "[%2d:%2d] mask: %08x endptcomplete: %08x %s %s", bit, ep,
+ (1 << bit), endptcomplete, (ARC_DIR_IN) ? "IN" : "OUT", ep ? "EPN" : "EP0");
+ switch (dir) {
+ case ARC_DIR_IN: /* Transmitting */
+ arc_dtd_releases (pcd, endpoint, ARC_DIR_IN);
+ if ((pcd_tx_complete_irq(endpoint, 0)))
+ arc_pcd_start_endpoint_in(pcd, endpoint);
+ break;
+
+ case ARC_DIR_OUT: /* Receiving */
+ remain_len = arc_read_rcv_buffer (pcd, endpoint);
+ arc_dtd_releases (pcd, endpoint, ARC_DIR_OUT);
+ if ( errors & DTD_STATUS_HALTED ) {
+ TRACE_MSG4(pcd->TAG, "[%2d:%2d] Current dtd is halted size_ioc_sts: %08x errors: %02x",
+ bit, ep, le32_to_cpu(cur_dtd->size_ioc_sts), errors);
+ cur_dtd->size_ioc_sts &= cpu_to_le32(errors);
+ consistent_sync(cur_dtd, sizeof(struct ep_td_struct), DMA_TO_DEVICE);
+ }
+
+ if ((pcd_rcv_complete_irq(endpoint, remain_len, 0))) {
+
+ UNLESS(pcd && pcd->bus && endpoint) {
+ printk(KERN_INFO"%s: NULL pcd: %p bus: %p endpoint: %p\n",
+ __FUNCTION__, pcd, pcd ? pcd->bus : NULL, endpoint);
+ break;
+ }
+ arc_pcd_start_endpoint_out(pcd, endpoint);
+ }
+ break;
+ }
+ endptcomplete &= ~(1 << bit);
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/*! arc_pcd_isr
+ */
+static irqreturn_t arc_pcd_isr(struct otg_dev *otg_dev, void *data, u32 mask)
+{
+ u32 irq_src;
+ struct otg_instance *otg = otg_dev ? otg_dev->otg_instance : NULL;
+ struct pcd_instance *pcd = otg_dev ? otg_dev->pcd_instance : NULL;
+ struct usbd_bus_instance *bus = pcd->bus;
+ int timeout;
+ irqreturn_t status = IRQ_NONE;
+
+ otg_led(LED2, 1);
+ otg->interrupts++;
+ if (TRACE_VERBOSE) {
+ TRACE_MSG0(pcd->TAG, "--");
+ TRACE_MSG5(pcd->TAG, "UOG_USBINTR: %08x UOG_ENDPTSTAT: %08x "
+ "UOG_ENDPTSETUPSTAT: %04x, UOG_ENDPTCOMPLETE: %08x, UOG_PORTSC1: %08x",
+ UOG_USBINTR, UOG_ENDPTSTAT, UOG_ENDPTSETUPSTAT, UOG_ENDPTCOMPLETE, UOG_PORTSC1);
+ }
+
+ /* verify controller is running */
+ RETURN_IRQ_NONE_UNLESS(UOG_USBCMD & USB_CMD_RUN_STOP);
+
+ /* get interrupt source and reset in USBSTS register */
+ irq_src = UOG_USBSTS & UOG_USBINTR;
+ UOG_USBSTS &= irq_src;
+ /* USB */
+ if (irq_src & USB_STS_INT) {
+ if ( (UOG_ENDPTSETUPSTAT & 0x1) || (UOG_ENDPTCOMPLETE & 0x1) || (UOG_ENDPTCOMPLETE & 0x10000))
+ status = arc_ep0_irq (pcd);
+ if ( (UOG_ENDPTCOMPLETE & 0xfffefffe) )
+ status = arc_epn_irq (pcd);
+ }
+ /* SOF */
+ if (irq_src & USB_STS_SOF) {
+ // TRACE_MSG0(pcd->TAG, "SOF interrupt");
+ status = IRQ_HANDLED;
+ }
+ /* port changed */
+ if (irq_src & USB_STS_PORT_CHANGE) {
+ u32 speed;
+ TRACE_MSG0(pcd->TAG, "PORT CHANGE interrupt");
+ if (!(UOG_PORTSC1 & PORTSCX_PORT_RESET)) {
+ speed = (UOG_PORTSC1 & PORTSCX_PORT_SPEED_MASK);
+ switch (speed) {
+ case PORTSCX_PORT_SPEED_HIGH:
+ TRACE_MSG0(pcd->TAG, "High Speed");
+ bus->high_speed = TRUE;
+ break;
+ case PORTSCX_PORT_SPEED_FULL:
+ TRACE_MSG0(pcd->TAG, "Full Speed");
+ bus->high_speed = FALSE;
+ break;
+ case PORTSCX_PORT_SPEED_LOW:
+ TRACE_MSG0(pcd->TAG, "Low Speed");
+ break;
+ default:
+ TRACE_MSG0(pcd->TAG, "Unknown Speed");
+ break;
+ }
+ status = IRQ_HANDLED;
+ }
+ if ((UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS)) {
+ TRACE_MSG0(pcd->TAG, "Cable is attached");
+ otg_event(otg, VBUS_VLD | B_SESS_VLD, otg->pcd->TAG, "ARC VBUS VALID");
+ status = IRQ_HANDLED;
+ }
+ if (!(UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS)) {
+ TRACE_MSG0(pcd->TAG, "Cable is detached");
+ otg_event(otg, VBUS_VLD_ | B_SESS_VLD_ | A_SESS_VLD_, otg->pcd->TAG, "ARC VBUS INVALID");
+ status = IRQ_HANDLED;
+ }
+ }
+ /* reset */
+ if (irq_src & USB_STS_RESET) {
+ u32 temp;
+ TRACE_MSG0(pcd->TAG, "RESET interrupt");
+
+ /* Reset address, endpoint status and complete */
+ UOG_DEVICEADDR &= ~USB_DEVICE_ADDRESS_MASK;
+ UOG_ENDPTSTAT = UOG_ENDPTSTAT;
+ UOG_ENDPTCOMPLETE = UOG_ENDPTCOMPLETE;
+
+ timeout = 10000000; // XXX
+ /* Wait until all endptprime bits cleared */
+ while (UOG_ENDPTPRIME && --timeout) { continue; }
+ if (timeout == 0) printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+
+ UOG_ENDPTFLUSH = 0xFFFFFFFF;
+
+ if (UOG_PORTSC1 & PORTSCX_PORT_RESET) {
+ TRACE_MSG0(pcd->TAG, "Bus reset");
+ }
+ else {
+ TRACE_MSG0(pcd->TAG, "Controller reset");
+ arc_udc_run ();
+ }
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_RESET, 0);
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_ADDRESS_ASSIGNED, 0);
+ status = IRQ_HANDLED;
+ }
+ /* SUSPEND / RESUME */
+ if (irq_src & USB_STS_SUSPEND) {
+ if (UOG_PORTSC1 & PORTSCX_PORT_SUSPEND) {
+ TRACE_MSG0(pcd->TAG, "SUSPEND interrupt");
+ pcd_check_device_feature(pcd->bus, USB_DEVICE_REMOTE_WAKEUP, 1);
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_INACTIVE, 0);
+ }
+ else {
+ TRACE_MSG0(pcd->TAG, "RESUME interrupt");
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_ACTIVITY, 0);
+ pcd_check_device_feature(pcd->bus, USB_DEVICE_REMOTE_WAKEUP, 0);
+ }
+ status = IRQ_HANDLED;
+ }
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+ TRACE_MSG0(pcd->TAG, "Error in interrupt");
+ status = IRQ_HANDLED;
+ }
+ if (TRACE_VERBOSE) TRACE_MSG6(pcd->TAG, "UOG_USBINTR: %08x UOG_ENDPTSTAT: %08x "
+ "UOG_ENDPTSETUPSTAT: %04x, UOG_ENDPTCOMPLETE: %08x, UOG_PORTSC1: %08x %s",
+ UOG_USBINTR, UOG_ENDPTSTAT, UOG_ENDPTSETUPSTAT, UOG_ENDPTCOMPLETE, UOG_PORTSC1,
+ (status == IRQ_HANDLED) ? "IRQ_HANDLED" : "IRQ_NONE");
+
+ otg_led(LED2, 0);
+ return status;
+}
+
+/*! default initialization
+ */
+int pcd_mod_init (struct otg_instance *otg);
+void pcd_mod_exit (struct otg_instance *otg);
+
+/*! arc_pcd_remove() - called to remove hardware
+ * @param otg_dev - otg device
+ */
+static void
+arc_pcd_remove(struct otg_dev *otg_dev)
+{
+ struct otg_instance *otg = otg_dev->otg_instance;
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct platform_device *pdev = to_platform_device(device);
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ TRACE_MSG0(otg->pcd->TAG, "--");
+ arc_udc_release ();
+ udc_controller = NULL;
+ pdata->platform_uninit(pdata);
+//printk(KERN_INFO"%s: jumpin116 start--------\n ", __FUNCTION__);
+ pcd_mod_exit(otg);
+//printk(KERN_INFO"%s: jumpin116 end--------- \n", __FUNCTION__);
+}
+
+/*! arc_pcd_probe() - called to probe hardware
+ * @param otg_dev - otg device
+ *
+ * This function should do minimal, one time only hardware recognition,
+ * resource reservation and minimal setup. Typically to get to known
+ * disabled state. It should not start the hardware.
+ *
+ */
+static int arc_pcd_probe(struct otg_dev *otg_dev)
+{
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct otg_instance *otg = otg_dev->otg_instance;
+ struct platform_device *pdev = to_platform_device(device);
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ usbd_pcd_ops.max_endpoints = UDC_MAX_ENDPOINTS;
+
+ RETURN_ENODEV_IF(strcmp(pdev->name, "arc_udc"));
+ UNLESS(pdata && pdata->platform_init) {
+ printk(KERN_INFO"%s: NO TRANSCEIVER CONFIGURED\n", __FUNCTION__);
+ return -ENODEV;
+ }
+ RETURN_ZERO_IF (pdata->platform_init(pdev));
+
+ //fsl_platform_set_vbus_power(pdata, 0);
+ RETURN_ENODEV_IF(pcd_mod_init(otg));
+ udc_controller = (struct arcotg_udc *) arc_udc_init (otg_dev);
+ return 0;
+}
+
+/*! arc_pcd_suspend
+ */
+static void arc_pcd_suspend(struct otg_dev *otg_dev, u32 state)
+{
+ /* NOTHING */
+}
+
+/*! arc_pcd_resume
+ */
+static void arc_pcd_resume(struct otg_dev *otg_dev)
+{
+ /* NOTHING */
+}
+
+/* ********************************************************************************************* */
+static struct otg_dev_driver arc_pcd_driver = {
+ .name = "arc_udc",
+ .id = OTG_DRIVER_PCD,
+ .probe = arc_pcd_probe,
+ .remove = arc_pcd_remove,
+ .suspend = arc_pcd_suspend,
+ .resume = arc_pcd_resume,
+ .irqs = (1 << 1), /* use irq at location #0 in udc_resources */
+ .isr = arc_pcd_isr,
+ .ops = &pcd_ops,
+};
+
+/*! arc_pcd_module_init() - module init
+ */
+int arc_pcd_module_init (struct otg_device_driver *otg_device_driver)
+{
+ return otg_dev_register_driver(otg_device_driver, &arc_pcd_driver);
+}
+
+/*! arc_pcd_module_exit() - module exit
+ */
+void arc_pcd_module_exit (struct otg_device_driver *otg_device_driver)
+{
+ otg_dev_unregister_driver (otg_device_driver, &arc_pcd_driver);
+}
diff --git a/drivers/otg/hardware/arc-tcd.c b/drivers/otg/hardware/arc-tcd.c
new file mode 100644
index 000000000000..64196b55fa27
--- /dev/null
+++ b/drivers/otg/hardware/arc-tcd.c
@@ -0,0 +1,260 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/arc-tcd.c -- USB Transceiver Controller driver
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/arc/arc-tcd.c|20070614183949|64278
+ *
+ * Copyright (c) 2006-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Shahrad Payandeh Lynne <sp@lbelcarra.com>,
+ * Stuart Lynne <sl@lbelcarra.com>,
+ *
+ */
+/*!
+ * @file otg/hardware/arc-tcd.c
+ * @brief Belcarra Freescale ARC Device driver.
+ *
+ * This interfaces to the generic Freescale platform transceiver driver.
+ *
+ *
+ * @ingroup ARC
+ * @ingroup TCD
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+#include <otg/otg-compat.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-dev.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-ocd.h>
+#include <otg/otg-pcd.h>
+#include <asm/arch/arc_otg.h>
+#include "arc-hardware.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+
+extern void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata, int on);
+
+/* ********************************************************************************************* */
+/*! arc_tcd_init() - used to enable
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ * Enable MX31 OTG interrupts.
+ *
+ */
+void arc_tcd_init(struct otg_instance *otg, u8 flag)
+{
+ struct otg_dev *otg_dev = otg->privdata;
+ struct tcd_instance *tcd = otg->tcd;
+
+ switch (flag) {
+ case SET:
+ break;
+ case RESET:
+ break;
+ }
+ otg_event(otg, OCD_OK, otg->tcd->TAG, "MX31 TCD OK");
+}
+
+/*! arc_tcd_en() - used to enable
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ * Sample OTG physical inputs and pass to OTG state machine.
+ *
+ */
+void arc_tcd_en(struct otg_instance *otg, u8 flag)
+{
+ struct otg_dev *otg_dev = otg->privdata;
+ struct tcd_instance *tcd = otg->tcd;
+
+ // XXX
+ u32 vbus = UOG_PORTSC1 & PORTSCX_CURRENT_CONNECT_STATUS;
+ TRACE_MSG2(tcd->TAG, "UOG_PORTSC1: %x, vbus: %x", UOG_PORTSC1, vbus);
+
+ switch (flag) {
+ case PULSE:
+ case SET:
+ switch (vbus) {
+ case 0:
+ otg_event(otg, VBUS_VLD_ | B_SESS_VLD_ | A_SESS_VLD_, otg->tcd->TAG, "MX31 VBUS INVALID");
+ break;
+ case 1:
+ otg_event(otg, VBUS_VLD | B_SESS_VLD, otg->tcd->TAG, "MX31 TCD EN");
+ break;
+ default:
+ break;
+ }
+ case RESET:
+ break;
+ default:
+ break;
+
+ }
+}
+
+
+/*! arc_dp_pullup_func - used to enable or disable peripheral connecting to bus
+ *
+ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5
+ *
+ * host peripheral
+ * d+ pull-up clr set
+ * d+ pull-down set clr
+ *
+ * d- pull-up clr clr
+ * d- pull-down set set
+ *
+ */
+
+
+/*! arc_dp_pullup_func
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ * Enable or disable pullup.
+ */
+void arc_dp_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ struct otg_dev *otg_dev = otg->privdata;
+
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "MX31 PULLUP SET");
+ UOG_USBCMD |= USB_CMD_RUN_STOP;
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "MX31 PULLUP RESET");
+ UOG_USBCMD &= ~USB_CMD_RUN_STOP;
+ break;
+ }
+}
+
+struct tcd_ops tcd_ops = {
+ .tcd_init_func = arc_tcd_init,
+ .tcd_en_func = arc_tcd_en,
+ .dp_pullup_func = arc_dp_pullup_func,
+};
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+int arc_transceiver_callback(struct otg_instance *otg, arc_belcarra_transceiver_event_t event)
+{
+ struct otg_dev *otg_dev = otg->privdata;
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct pcd_instance *pcd_instance = otg_dev->pcd_instance;
+
+ switch (event) {
+ case arc_vbus_valid:
+ TRACE_MSG1(otg->tcd->TAG, "VBUS VALID event: %d", event);
+ otg_event(otg, VBUS_VLD | B_SESS_VLD, otg->tcd->TAG, "MX31 VBUS VALID");
+ break;
+ case arc_vbus_invalid:
+ TRACE_MSG1(otg->tcd->TAG, "VBUS INVALID event: %d", event);
+ otg_event(otg, VBUS_VLD_ | B_SESS_VLD_ | A_SESS_VLD_, otg->tcd->TAG, "MX31 VBUS INVALID");
+ break;
+ }
+ return 0;
+}
+
+/*!
+ * arc_tcd_remove() - called to remove hardware
+ * @param otg_dev - otg device
+ */
+static void
+arc_tcd_remove(struct otg_dev *otg_dev)
+{
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct platform_device *pdev = to_platform_device(device);
+
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+}
+
+/*!
+ * arc_tcd_probe() - called to probe hardware
+ * @param otg_dev - otg device
+ *
+ * This function should do minimal, one time only hardware recognition,
+ * resource reservation and minimal setup. Typically to get to known
+ * disabled state. It should not start the hardware.
+ *
+ */
+static int
+arc_tcd_probe(struct otg_dev *otg_dev)
+{
+ struct device *device = otg_dev_get_drvdata(otg_dev);
+ struct platform_device *pdev = to_platform_device(device);
+
+ struct fsl_usb2_platform_data *pdata = (struct fsl_usb2_platform_data*)pdev->dev.platform_data;
+
+ struct otg_instance *otg = otg_dev->otg_instance;
+ struct tcd_instance *tcd = otg->tcd;
+
+ RETURN_ENODEV_IF(strcmp(pdev->name, "arc_udc"));
+ RETURN_ZERO_IF (pdata->platform_init(pdev));
+
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/* arc tcd otg_dev_driver structure
+ */
+static struct otg_dev_driver arc_tcd_driver = {
+ .name = "arc_udc",
+ .id = OTG_DRIVER_TCD,
+
+ .probe = arc_tcd_probe,
+ .remove = arc_tcd_remove,
+
+ .ops = &tcd_ops,
+};
+
+/* ********************************************************************************************* */
+
+/*! arc_tcd_module_init() - module init
+ */
+int arc_tcd_module_init (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return otg_dev_register_driver(otg_device_driver, &arc_tcd_driver);
+}
+
+/*!
+ * arc_tcd_module_exit() - module exit
+ */
+void arc_tcd_module_exit (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ otg_dev_unregister_driver (otg_device_driver, &arc_tcd_driver);
+}
diff --git a/drivers/otg/hardware/arc.h b/drivers/otg/hardware/arc.h
new file mode 100644
index 000000000000..070109135e76
--- /dev/null
+++ b/drivers/otg/hardware/arc.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/arc.h - MX31 Device driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/arc/arc.h|20070801221240|58512
+ *
+ * Copyright (c) 2006-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ */
+/*!
+ * @defgroup ARC Freescale HS (ARC)
+ * @ingroup Hardware
+ */
+/*!
+ * @file otg/hardware/arc.h
+ * @brief Belcarra Freescale ARC Device driver.
+ *
+ * @ingroup ARC
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+
+
+struct mx31_dtd_list_struct {
+ struct list_head td_queue;
+ struct ep_td_struct *td;
+ int primed;
+ int epnum;
+ int dir;
+};
+
+struct arc_private_struct {
+ struct ep_queue_head *cur_dqh;
+ struct ep_td_struct *cur_dtd;
+};
+
+struct arcotg_udc {
+ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
+ struct ep_td_struct *ep_dtd;
+};
diff --git a/drivers/otg/hardware/i2c-l26.c b/drivers/otg/hardware/i2c-l26.c
new file mode 100644
index 000000000000..6959136a6735
--- /dev/null
+++ b/drivers/otg/hardware/i2c-l26.c
@@ -0,0 +1,295 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/i2c-l26.c -- Linux 2.6 I2C access
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/otglib/i2c-l26.c|20070614183950|36713
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/i2c-l26.c
+ * @brief Linux I2C I/O via generic i2c device.
+ *
+ * Writes are queued and performed in a bottom half handler.
+ *
+ * @ingroup ISP1301TCD
+ * @ingroup LINUXOS
+ */
+
+#include <otg/otg-compat.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/i2c.h>
+
+#include <otg/pcd-include.h>
+#include <linux/pci.h>
+#include "isp1301-hardware.h"
+#include "isp1301.h"
+
+/* ********************************************************************************************* */
+
+/*
+ * N.B. i2c functions must not be called from interrupt handlers
+ */
+
+static struct file *i2c_file;
+static struct i2c_client *i2c_client;
+static int initstate_i2c;
+static int initstate_region;
+#define MAX_I2C 16
+
+/*! i2c_configure
+ * Attempt to find and open generic i2c device
+ * @param name - i2c device name
+ * @param addr - address used to configure i2c
+ * @return - non-zero for failure
+ */
+int i2c_configure(char *name, int addr)
+{
+ char filename[20];
+ struct i2c_adapter *ad;
+ int tmp;
+
+ RETURN_ZERO_IF(initstate_i2c);
+
+#if 1
+ for (tmp=0 ; tmp<MAX_I2C; tmp++){
+ ad = i2c_get_adapter(tmp);
+ if (ad){
+// printk(KERN_INFO"SHP - for tmp = %d name = %s\n", tmp, ad->name);
+ if (!strncmp(ad->name, name, strlen(name)))
+ break;
+ }// valid driver
+ }
+ if (tmp == MAX_I2C) { // Nothing found
+ printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__);
+ return -ENODEV;
+ }
+// printk(KERN_ERR"%s: kmalloc(sizeof(*i2c_client) ", __FUNCTION__);
+ i2c_client = kmalloc(sizeof(*i2c_client), GFP_KERNEL);
+ i2c_client->adapter = (struct i2c_adapter *) ad;
+ // printk(KERN_INFO"i2c_client name = %s\n", i2c_client->adapter->name);
+#endif
+
+#if 0
+ /*find the I2C driver we need
+ */
+ for (tmp = 0; tmp < MAX_I2C; tmp++) {
+
+ sprintf(filename, "/dev/i2c/%d", tmp);
+
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, filename);
+
+ UNLESS (IS_ERR(i2c_file = filp_open(filename, O_RDWR, 0))) {
+
+ //printk(KERN_INFO"%s: %s found\n", __FUNCTION__, filename);
+
+ /*found some driver */
+ i2c_client = (struct i2c_client *)i2c_file->private_data;
+
+ printk(KERN_INFO"%s: \"%s\" found \"%s\"\n", __FUNCTION__, name, i2c_client->adapter->name);
+ if (strlen(i2c_client->adapter->name) >= 8) {
+ if (!strncmp(i2c_client->adapter->name, name, strlen(name)))
+ break; /*we found our driver! */
+ }
+ i2c_client = NULL;
+ filp_close(i2c_file, NULL);
+ }
+ printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, filename, i2c_file);
+ }
+ if (tmp == MAX_I2C) { // Nothing found
+ printk(KERN_ERR"%s: cannot find I2C driver", __FUNCTION__);
+ return -ENODEV;
+ }
+#endif
+
+ i2c_client->addr = addr;
+ initstate_i2c = 1;
+ return 0;
+}
+
+
+
+/*! i2c_close
+ * Close i2c device fd.
+ */
+void i2c_close(void)
+{
+#if 1
+ printk(KERN_ERR"%s: Free i2c_client \n", __FUNCTION__);
+ kfree(i2c_client);
+#endif
+#if 0
+ if (initstate_i2c)
+ filp_close(i2c_file, NULL);
+#endif
+ initstate_i2c = 0;
+}
+
+/*! i2c_readb
+ * Read byte from i2c device
+ * @param subaddr - address to read value
+ * @return read byte value
+ */
+u8 i2c_readb(u8 subaddr)
+{
+ u8 buf = 0;
+ i2c_master_send(i2c_client, &subaddr, 1);
+ i2c_master_recv(i2c_client, &buf, 1);
+ //TRACE_MSG2(TCD, "addr: %02x buf: %02x", subaddr, buf);
+ return buf;
+}
+
+/*! i2c_readw -
+ * Read word from i2c device
+ * @param subaddr - address to fetch value
+ * @return fetched word value
+ */
+u16 i2c_readw(u8 subaddr)
+{
+ u16 buf = 0;
+ i2c_master_send(i2c_client, &subaddr, 1);
+ i2c_master_recv(i2c_client, (u8 *)&buf, 2);
+ //TRACE_MSG2(TCD, "addr: %02x buf: %04x", subaddr, buf);
+ return buf;
+}
+
+/*! i2c_readl -
+ * Read long from i2c device
+ * @param subaddr - address to fetch value
+ * @return fetched value
+ */
+u32 i2c_readl(u8 subaddr)
+{
+ u32 buf = 0;
+ i2c_master_send(i2c_client, &subaddr, 1);
+ i2c_master_recv(i2c_client, (u8 *)&buf, 4);
+ //TRACE_MSG2(TCD, "addr: %02x buf: %08x", subaddr, buf);
+ return buf;
+}
+
+
+
+/* ********************************************************************************************* */
+/*! @var struct otg_task i2c_io_task
+ * @brief - an otg_task intance to process i2c_io operations
+ */
+struct otg_task *i2c_io_task;
+
+#define I2C_MAX_WRITE 500
+
+int i2c_Write_Queued;
+u8 i2c_Write_Data[I2C_MAX_WRITE];
+u8 i2c_Write_Addr[I2C_MAX_WRITE];
+
+/*! i2c_writeb_direct() - internal
+ * Writw byte to i2c device
+ * @param subaddr
+ * @param buf
+ */
+void i2c_writeb_direct(u8 subaddr, u8 buf)
+{
+ char tmpbuf[2];
+
+ tmpbuf[0] = subaddr; /*register number */
+ tmpbuf[1] = buf; /*register data */
+ i2c_master_send(i2c_client, &tmpbuf[0], 2);
+}
+
+/*!
+ * i2c_io_bh() - bottom half handler to use i2c_write on queued data
+ * i2c_write operations are queued so that they can be done in this bottom
+ * half handler.
+ *
+ * XXX the memcpy's could be eliminated with head/tail pointers.
+ *
+ * @param data
+ */
+void *i2c_io_bh(otg_task_arg_t data)
+{
+ while (i2c_Write_Queued > 0) {
+ u8 write = i2c_Write_Data[0];
+ u8 port = i2c_Write_Addr[0];
+ unsigned long flags; // atomic update of counter and saved values
+ local_irq_save (flags);
+ //printk(KERN_INFO"%s: i2c_Write_Queued: %d\n", __FUNCTION__, i2c_Write_Queued);
+ i2c_Write_Queued--;
+ memcpy(i2c_Write_Data, i2c_Write_Data + 1, sizeof(i2c_Write_Data) - 1);
+ memcpy(i2c_Write_Addr, i2c_Write_Addr + 1, sizeof(i2c_Write_Addr) - 4);
+ local_irq_restore (flags);
+
+ i2c_writeb_direct(port, write); // perform write
+
+ //TRACE_MSG3(TCD,"port: %02x data: %02x result: %02x", port, write, i2c_readb(port & 0xfe));
+ }
+ return NULL;
+}
+
+
+/*!
+ * i2c_writeb() - queue write operation
+ * @param port
+ * @param data byte to write
+ */
+void i2c_writeb(u8 port, u8 data)
+{
+ unsigned long flags; // atomic update of counter and saved values
+ RETURN_IF(i2c_Write_Queued < 0); // check if terminating
+ if ((i2c_Write_Queued == I2C_MAX_WRITE)) {
+
+ printk(KERN_ERR"TOO MANY QUEUED CANNOT WRITE port: %02x data: %02x active: %d\n", port, data, i2c_Write_Queued);
+ //TRACE_MSG3(TCD, "TOO MANY QUEUED CANNOT WRITE port: %02x data: %02x active: %d", port, data, i2c_Write_Queued);
+ return;
+ }
+ local_irq_save (flags);
+ i2c_Write_Addr[i2c_Write_Queued] = port;
+ i2c_Write_Data[i2c_Write_Queued] = data;
+ i2c_Write_Queued++;
+ //printk(KERN_INFO"%s: i2c_Write_Queued: %d\n", __FUNCTION__, i2c_Write_Queued);
+ local_irq_restore (flags);
+ //TRACE_MSG3(TCD, "port: %02x data: %02x active: %d", port, data, i2c_Write_Queued);
+ //SCHEDULE_WORK(i2c_io_work_struct);
+ if (i2c_io_task) otg_up_work(i2c_io_task);
+}
+
+
+int i2c_mod_init(struct otg_instance *otg)
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //PREPARE_WORK_ITEM(i2c_io_work_struct, &i2c_io_bh, NULL);
+ TRACE_MSG0(otg->tcd->TAG, "INIT");
+ RETURN_EINVAL_UNLESS((i2c_io_task = otg_task_init2("otgi2c", i2c_io_bh, NULL, otg->tcd->TAG)));
+ //i2c_io_task->debug = TRUE;
+ otg_task_start(i2c_io_task);
+ return 0;
+}
+
+void i2c_mod_exit(struct otg_instance *otg)
+{
+ otg_task_exit(i2c_io_task);
+ i2c_io_task = NULL;
+
+ TRACE_MSG0(otg->tcd->TAG, "EXIT");
+ //while (PENDING_WORK_ITEM(i2c_io_work_struct) /*|| PENDING_WORK_ITEM(i2c_xcvr_work_struct)*/ ) {
+ // printk(KERN_ERR"%s: waiting for bh\n", __FUNCTION__);
+ // schedule_timeout(10 * HZ);
+ //}
+}
diff --git a/drivers/otg/hardware/imx31ads-l26.c b/drivers/otg/hardware/imx31ads-l26.c
new file mode 100644
index 000000000000..bee89f11194e
--- /dev/null
+++ b/drivers/otg/hardware/imx31ads-l26.c
@@ -0,0 +1,184 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mx31-l26.c - iMX31ADS EVB OTG Peripheral and OTG Controller Drivers Module Initialization
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/imx31ads/imx31ads-l26.c|20070909224442|14233
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @defgroup MX31 High Speed
+ * @ingroup Hardware
+ */
+/*!
+ * @file otg/hardware/imx31ads-l26.c
+ * @brief MX31 USB Host Controller Driver
+ *
+ * Linux MX31 OTG PCD/OCD/TCD Driver Initialization
+ *
+ * This file selects and initializes all of the low level hardware drivers
+ * for the MX31 High Speed.
+ *
+ * Notes.
+ *
+ *
+ * @ingroup MX31
+ * @ingroup MXC
+ * @ingroup LINUXOS
+ */
+
+#include <otg/pcd-include.h>
+#include <otg/otg-dev.h>
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <asm/arch/gpio.h>
+
+//#include "mxc-lnx.h"
+//#include "mxc-hardware.h"
+
+MOD_AUTHOR ("sl@belcarra.com");
+MOD_DESCRIPTION ("Belcarra MX31");
+EMBED_LICENSE();
+
+
+MOD_PARM_STR(serial_number_str, "Serial Number String", NULL);
+
+
+/* ************************************************************************************* */
+
+extern int arc_dev_module_init(struct otg_device_driver *otg_device_driver,
+ int (*device_probe)(struct device *),
+ int (*device_remove)(struct device *)
+ );
+
+extern void arc_dev_module_exit(struct otg_device_driver *);
+
+extern int arc_pcd_module_init (struct otg_device_driver *);
+extern void arc_pcd_module_exit (struct otg_device_driver *);
+
+//extern int arc_tcd_module_init (struct otg_device_driver *);
+//extern void arc_tcd_module_exit (struct otg_device_driver *);
+
+//extern int arc_ocd_module_init (struct otg_device_driver *otg_device_driver);
+//extern void arc_ocd_module_exit (struct otg_device_driver *otg_device_driver);
+
+static struct otg_device_driver mx31_otg_device_driver = {
+ .name = "arc_udc",
+};
+
+
+
+/*! arc_dev_probe - called to initialize platform
+ * @param device - device
+ *
+ * This is used to call the dev level probe functions with
+ * the additional otg_device_driver structure.
+ */
+static int
+arc_dev_probe(struct device *device)
+{
+ printk(KERN_INFO"%s: l26\n", __FUNCTION__);
+ RETURN_ZERO_UNLESS (mx31_otg_device_driver.probe);
+ return mx31_otg_device_driver.probe(device, &mx31_otg_device_driver);
+}
+
+/*! arc_dev_remove- called to remote device
+ * @param device - device
+ *
+ * This is used to call the dev level probe functions with
+ * the additional otg_device_driver structure.
+ */
+static int
+arc_dev_remove(struct device *device)
+{
+ int rc;
+ printk(KERN_INFO"%s: l26\n", __FUNCTION__);
+ RETURN_ZERO_UNLESS (mx31_otg_device_driver.remove);
+ rc = mx31_otg_device_driver.remove(device, &mx31_otg_device_driver);
+ return rc;
+}
+
+
+/*!
+ * mx31_modexit() - This is used as module exit, and as cleanup if modinit fails.
+ */
+static void mx31_modexit (void)
+{
+ /* unload the dev driver, this will stop otg and destroy
+ * the otg_dev and otg instances etc.
+ */
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ arc_dev_module_exit(&mx31_otg_device_driver);
+
+ /* cleanup the rest of the sub-drivers
+ */
+ arc_pcd_module_exit(&mx31_otg_device_driver);
+ //arc_tcd_module_exit(&mx31_otg_device_driver);
+ //arc_ocd_module_exit(&mx31_otg_device_driver);
+}
+
+/*!
+ * mx31_modinit() - linux module initialization
+ *
+ * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd
+ * for some architectures.
+ *
+ */
+static int mx31_modinit (void)
+{
+ int ocd = -1, tcd = -1, pcd = -1, dev = -1;
+
+ /* initialize all of the sub-drivers except for dev
+ */
+ //ocd = arc_ocd_module_init(&mx31_otg_device_driver);
+ //tcd = arc_tcd_module_init(&mx31_otg_device_driver);
+ pcd = arc_pcd_module_init(&mx31_otg_device_driver);
+
+ /* ensure everything is ok until now */
+ //THROW_IF(ocd || tcd || pcd, error);
+ THROW_IF(pcd, error);
+
+ /* serial number needs to be set prior to initializing the dev
+ * driver
+ */
+ mx31_otg_device_driver.serial_number = MODPARM(serial_number_str);
+
+ /* initialize the dev driver, this will get all sub-drivers
+ * started via their probe functions, create otg and otg_dev
+ * instances and finally start otg state machine.
+ */
+ THROW_IF((dev = arc_dev_module_init(&mx31_otg_device_driver, arc_dev_probe, arc_dev_remove)), error);
+
+ return 0;
+
+ CATCH(error) {
+
+ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
+
+ UNLESS (dev) arc_dev_module_exit(&mx31_otg_device_driver);
+ //UNLESS (tcd) arc_tcd_module_exit(&mx31_otg_device_driver);
+ UNLESS (pcd) arc_pcd_module_exit(&mx31_otg_device_driver);
+ //UNLESS (ocd) arc_ocd_module_exit(&mx31_otg_device_driver);
+
+ return -EINVAL;
+ }
+}
+
+module_init (mx31_modinit);
+module_exit (mx31_modexit);
diff --git a/drivers/otg/hardware/isp1301-hardware.h b/drivers/otg/hardware/isp1301-hardware.h
new file mode 100644
index 000000000000..5e15e02b093a
--- /dev/null
+++ b/drivers/otg/hardware/isp1301-hardware.h
@@ -0,0 +1,227 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/isp1301-hardware.h -- ISP1301 hardware specific defines
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/isp1301/isp1301-hardware.h|20061123215517|03742
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/isp1301-hardware.h
+ * @brief Public Structures And Defines For ISP1301 Hardware.
+ *
+ * The Philips ISP1301 is an OTG Transceiver. There a additional
+ * compatible parts such as the Maxim MAX3301E
+ *
+ * @ingroup ISP1301TCD
+ */
+
+
+/*!
+ * @name ADR/PSW - C.f. 8.9.1
+ *
+ * The i2c address is either 0x2c or 0x2d depending on the level of the
+ * ADR/PSW pin after device reset. Note that this pin can be programmed as
+ * an output via the Mode Control 2 register, bit PSW_OE to enable an
+ * @{
+ */
+
+#define ISP1301_I2C_ADDR_LOW 0x2c
+#define ISP1301_I2C_ADDR_HIGH 0x2d
+/*! @} */
+
+/*!
+ * @name ISP1301 Registers C.f. 11.1
+ * @{
+ */
+
+#define ISP1301_VENDOR_ID 0x00
+#define ISP1301_PRODUCT_ID 0x02
+#define ISP1301_VERSION_ID 0x14
+
+#define ISP1301_VENDOR_ID_LOW 0x00
+#define ISP1301_PRODUCT_ID_LOW 0x02
+#define ISP1301_VERSION_ID_LOW 0x14
+#define ISP1301_VENDOR_ID_HIGH 0x01
+#define ISP1301_PRODUCT_ID_HIGH 0x03
+#define ISP1301_VERSION_ID_HIGH 0x15
+
+/* these are all single byte registers
+ */
+#define ISP1301_MODE_CONTROL_1 0x04
+#define ISP1301_MODE_CONTROL_1_SET 0x04
+#define ISP1301_MODE_CONTROL_1_CLR 0x05
+
+#define ISP1301_MODE_CONTROL_2 0x12
+#define ISP1301_MODE_CONTROL_2_SET 0x12
+#define ISP1301_MODE_CONTROL_2_CLR 0x13
+
+#define ISP1301_OTG_CONTROL 0x06
+#define ISP1301_OTG_CONTROL_SET 0x06
+#define ISP1301_OTG_CONTROL_CLR 0x07
+
+#define ISP1301_OTG_STATUS 0x10
+
+#define ISP1301_INTERRUPT_SOURCE 0x08
+
+#define ISP1301_INTERRUPT_LATCH 0x0a
+#define ISP1301_INTERRUPT_LATCH_SET 0x0a
+#define ISP1301_INTERRUPT_LATCH_CLR 0x0b
+
+#define ISP1301_INTERRUPT_ENABLE_LOW 0x0c
+#define ISP1301_INTERRUPT_ENABLE_LOW_SET 0x0c
+#define ISP1301_INTERRUPT_ENABLE_LOW_CLR 0x0d
+
+#define ISP1301_INTERRUPT_ENABLE_HIGH 0x0e
+#define ISP1301_INTERRUPT_ENABLE_HIGH_SET 0x0e
+#define ISP1301_INTERRUPT_ENABLE_HIGH_CLR 0x0f
+/*! @} */
+
+
+/*!
+ * @name Mode Control 1 register - C.f. Table 17 and Table 18
+ * @{
+ */
+#define ISP1301_UART_EN (1 << 6)
+#define ISP1301_OE_INT_EN (1 << 5)
+#define ISP1301_BDIS_ACON_EN (1 << 4)
+#define ISP1301_TRANSP_EN (1 << 3)
+#define ISP1301_DAT_SE0 (1 << 2)
+#define ISP1301_SUSPEND_REG (1 << 1)
+#define ISP1301_SPEED_REG (1 << 0)
+/*! @} */
+
+
+/*!
+ * @name Mode Control 2 register - C.f. Table 19 and Table 20
+ * @{
+ */
+#define ISP1301_EN2V7 (1 << 7)
+#define ISP1301_PSW_OE (1 << 6)
+#define ISP1301_AUDIO_EN (1 << 5)
+#define ISP1301_TRANSP_BDIR (3 << 3)
+#define ISP1301_BI_DI (1 << 2)
+#define ISP1301_SPD_SUSP_CTRL (1 << 1)
+#define ISP1301_GLOBAL_PWR_ON (1 << 0)
+/*! @} */
+
+
+/*!
+ * @name OTG Control register - C.f. Table 21 and Table 22
+ * @{
+ */
+#define ISP1301_VBUS_CHRG (1 << 7)
+#define ISP1301_VBUS_DISCHRG (1 << 6)
+#define ISP1301_VBUS_DRV (1 << 5)
+#define ISP1301_ID_PULLDOWN (1 << 4)
+#define ISP1301_DM_PULLDOWN (1 << 3)
+#define ISP1301_DP_PULLDOWN (1 << 2)
+#define ISP1301_DM_PULLUP (1 << 1)
+#define ISP1301_DP_PULLUP (1 << 0)
+
+#define ISP1301_VBUS_RESET (ISP1301_VBUS_CHRG | ISP1301_VBUS_DISCHRG | ISP1301_VBUS_DRV)
+
+/*! @} */
+
+
+/*!
+ * @name OTG Status register - C.f. Table 23 and Table 24
+ * @{
+ */
+#define ISP1301_B_SESS_VLD (1 << 7)
+#define ISP1301_B_SESS_END (1 << 6)
+/*! @} */
+
+
+/*!
+ * @name Interrupt Source Register
+ * Interrupt Source register - C.f. Table 25 and Table 26
+ * Interrupt Latch register - C.f. Table 27 and Table 28
+ * Interrupt Enable Low register - C.f. Table 29 and Table 30
+ * Interrupt Enable High register - C.f. Table 31 and Table 32
+ * @{
+ */
+#define ISP1301_CR_INT (1 << 7)
+#define ISP1301_BDIS_ACON (1 << 6)
+#define ISP1301_ID_FLOAT (1 << 5)
+#define ISP1301_DM_HI (1 << 4)
+#define ISP1301_ID_GND (1 << 3)
+#define ISP1301_DP_HI (1 << 2)
+#define ISP1301_SESS_VLD (1 << 1)
+#define ISP1301_VBUS_VLD (1 << 0)
+
+//#define ISP1301_SE1 (ISP1301_DM_HI | ISP1301_DP_HI)
+
+/*! @} */
+
+/*!
+ * @name Maxim MAX3301E
+ *
+ * This part is compatible with the ISP1301 with minor differences:
+ *
+ * Mode Control 1 register is called Control Register 1. It is almost
+ * identical except for:
+ *
+ * bit isp1301 max3301e
+ * 3 transp_en not used
+ * @{
+ */
+#define MAX3301E_CONTROL_REGISTER_1 0x04
+#define MAX3301E_CONTROL_REGISTER_1_SET 0x04
+#define MAX3301E_CONTROL_REGISTER_1_CLR 0x05
+/*! @} */
+
+/*!
+ * @name Mode Control 2
+ * Mode Control 2 is called Special Function Register 1. Mostly the same
+ * except for:
+ *
+ * bit isp1301 max3301e
+ * 7 en2v7 gp_en
+ * 6 psw_oe sess_end
+ * 5 audio_en int_source
+ * @{
+ */
+#define MAX3301E_SPECIAL_FUNCTION_1 0x12
+#define MAX3301E_SPECIAL_FUNCTION_1_SET 0x12
+#define MAX3301E_SPECIAL_FUNCTION_1_CLR 0x13
+#define MAX3301E_GP_EN (1 << 7)
+#define MAX3301E_SESS_END (1 << 6)
+#define MAX3301E_INT_SOURCE (1 << 5)
+/*! @} */
+
+/*!
+ * @name OTG Status
+ * The OTG Status is not present, but B_SESS_END seems to be available
+ * in the Mode Control 2 register bit 6.
+ *
+ * An additional register is available for MAX3301E specific features.
+ *
+ * Special Function Register 2
+ * @{
+ */
+
+#define MAX3301E_SPECIAL_FUNCTION_2 0x16
+#define MAX3301E_SPECIAL_FUNCTION_2_SET 0x16
+#define MAX3301E_SPECIAL_FUNCTION_2_CLR 0x17
+#define MAX3301E_SDWN (1 << 0)
+#define MAX3301E_IRQ_MODE (1 << 1)
+#define MAX3301E_XCVR_INPUT_DISC (1 << 2)
+#define MAX3301E_REQ_SET (1 << 3)
+/*! @} */
diff --git a/drivers/otg/hardware/isp1301-procfs.c b/drivers/otg/hardware/isp1301-procfs.c
new file mode 100644
index 000000000000..726e1d7bf782
--- /dev/null
+++ b/drivers/otg/hardware/isp1301-procfs.c
@@ -0,0 +1,468 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/isp1301-procfs.c - USB Device Core Layer
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/isp1301/isp1301-procfs.c|20070612233038|36770
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*!
+ * @file otg/hardware/isp1301-procfs.c
+ * @brief Implement /proc/isp1301 to dump ISP1301 registers.
+ *
+ *
+ * @ingroup ISP1301TCD
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX)
+
+#include <otg/pcd-include.h>
+#include "isp1301-hardware.h"
+#include "isp1301.h"
+
+#ifdef CONFIG_ARCH_MX2ADS
+#include <asm/arch/mx2.h>
+#define MX2_OTG_XCVR_DEVAD 0x18
+#define MX2_SEQ_OP_REG 0x19
+#define MX2_SEQ_RD_STARTAD 0x1a
+#define MX2_I2C_OP_CTRL_REG 0x1b
+#define MX2_SCLK_TO_SCL_HPER 0x1e
+#define MX2_I2C_INTERRUPT_AND_CTRL 0x1f
+
+#define OTG_BASE_ADDR 0x10024000
+//#define OTG_I2C_BASE (OTG_BASE_ADDR+0x100)
+
+#endif /* CONFIG_ARCH_MX2ADS */
+
+#if defined(CONFIG_OTG_ISP1301_PROCFS) || defined(_OTG_DOXYGEN)
+/* Proc Filesystem *************************************************************************** */
+
+extern struct isp1301_private isp1301_private;
+
+#define MAX_HISTORY 6
+/*! @fn struct reg_list
+ * @brief - struct definition for register
+ */
+struct reg_list {
+ u8 reg;
+ u8 size;
+ char *name;
+ u32 values[MAX_HISTORY];
+};
+
+#define REG(r, s) {r, s, #r, }
+
+/*! @name register list tables
+ *
+ * @{
+ */
+
+struct reg_list isp1301_prod_list[] = {
+ REG(ISP1301_VENDOR_ID, 2),
+ REG(ISP1301_PRODUCT_ID, 2),
+ REG(ISP1301_VERSION_ID, 2),
+ { 0, 1, NULL,},
+};
+struct reg_list isp1301_reg_list[] = {
+ REG(ISP1301_OTG_CONTROL_SET, 1),
+ REG(ISP1301_INTERRUPT_SOURCE, 1),
+ REG(ISP1301_INTERRUPT_LATCH_SET, 1),
+ REG(ISP1301_INTERRUPT_ENABLE_LOW_SET, 1),
+ REG(ISP1301_INTERRUPT_ENABLE_HIGH_SET, 1),
+ REG(ISP1301_MODE_CONTROL_1_SET, 1),
+ { 0, 1, NULL,},
+};
+struct reg_list isp1301_spec_list[] = {
+ REG(ISP1301_MODE_CONTROL_2_SET, 1),
+ REG(ISP1301_OTG_STATUS, 1),
+ { 0, 1, NULL,},
+};
+struct reg_list max3301e_spec_list[] = {
+ REG(MAX3301E_SPECIAL_FUNCTION_1_SET, 1),
+ REG(MAX3301E_SPECIAL_FUNCTION_2_SET, 1),
+ { 0, 1, NULL,},
+};
+#ifdef CONFIG_ARCH_MX2ADS
+struct reg_list mx21_spec_list[] = {
+ REG(MX2_OTG_XCVR_DEVAD, 1),
+ REG(MX2_SEQ_OP_REG, 1),
+ REG(MX2_SEQ_RD_STARTAD, 1),
+ REG(MX2_I2C_OP_CTRL_REG, 1),
+ REG(MX2_SCLK_TO_SCL_HPER, 1),
+ REG(MX2_I2C_INTERRUPT_AND_CTRL, 1),
+ { 0, 1, NULL,},
+};
+/* @} */
+#endif /* CONFIG_ARCH_MX2ADS */
+/*! isp1301_update -
+ * @param list - isp1301 registers table
+ */
+void isp1301_update(struct reg_list *list)
+{
+ for (; list && list->name; list++) {
+ //TRACE_MSG1(TCD, "list: %s", list->name);
+ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32));
+ switch(list->size) {
+ case 1:
+ list->values[0] = i2c_readb(list->reg);
+ break;
+ case 2:
+ list->values[0] = i2c_readw(list->reg);
+ break;
+ case 4:
+ list->values[0] = i2c_readl(list->reg);
+ break;
+ }
+ }
+}
+
+#ifdef CONFIG_ARCH_MX2ADS
+/*! mx2_rb - read a byte value from I2C device port
+ * @param port - device port value
+ * @return - read byte value
+ */
+static u8 __inline__ mx2_rb(u32 port)
+{
+ return *(volatile u8 *) (MX2_IO_ADDRESS(port + OTG_I2C_BASE));
+}
+
+/*! mx21_update - update an reg_list
+ * @param list - pointer to reg_list to update
+ */
+void mx21_update(struct reg_list *list)
+{
+ for (; list && list->name; list++) {
+ memmove(list->values + 1, list->values, sizeof(list->values) - sizeof(u32));
+ list->values[0] = mx2_rb(list->reg);
+ }
+}
+
+#endif /* CONFIG_ARCH_MX2ADS */
+/*!
+ * isp1301_update_all - update all reg_list
+ */
+void isp1301_update_all(void)
+{
+ isp1301_update(isp1301_reg_list);
+ switch (isp1301_private.transceiver_map->transceiver_type) {
+ case isp1301:
+ isp1301_update(isp1301_spec_list);
+ break;
+ case max3301e:
+ isp1301_update(max3301e_spec_list);
+ break;
+ default:
+ break;
+ }
+#ifdef CONFIG_ARCH_MX2ADS
+ mx21_update(mx21_spec_list);
+#endif /* CONFIG_ARCH_MX2ADS */
+
+}
+
+
+
+/*!
+ * dohexdigit - translate a value to hex notation
+ * @param cp - buffer to save translated value
+ * @param val - value to translate
+ *
+ */
+static void dohexdigit (char *cp, unsigned char val)
+{
+ if (val < 0xa)
+ *cp = val + '0';
+ else if ((val >= 0x0a) && (val <= 0x0f))
+ *cp = val - 0x0a + 'a';
+}
+
+/*!
+ * dohexval - translate a value to hex notation
+ * @param cp - buffer to save translated value
+ * @param val - value to translate
+ *
+ */
+static void dohexval (char *cp, unsigned char val)
+{
+ dohexdigit (cp++, val >> 4);
+ dohexdigit (cp++, val & 0xf);
+}
+
+/*! isp_1301_dump - dump a reg_list instance information
+ * @param buf - place to store information
+ * @param name -
+ * @param fmt
+ * @param reg
+ * @return
+ */
+int isp1301_dump(char *buf, char *name, char *fmt, struct reg_list *reg)
+{
+ int len = 0, i;
+ len += sprintf (buf + len, "%-20s %-34s [%03x]: ", name, reg->name, reg->reg);
+ len += sprintf (buf + len, fmt, reg->values[0]);
+ for (i = 1; i < MAX_HISTORY; i++)
+ if (reg->values[i - 1] == reg->values[i])
+ len += sprintf (buf + len, " ");
+ else
+ len += sprintf (buf + len, fmt, reg->values[i]);
+ len += sprintf (buf + len, "\n");
+ return len;
+}
+/*! isp1301_dump_list - dump reg_list table's inforamtion
+ * @param buf -
+ * @param name
+ * @param list
+ * @return information buffer length
+ */
+int isp1301_dump_list(char * buf, char *name, struct reg_list *list)
+{
+ int len = 0;
+ for (; list && list->name; list++)
+ switch(list->size) {
+ case 1: len += isp1301_dump(buf + len, name, " %02x", list); break;
+ case 2: len += isp1301_dump(buf + len, name, " %04x", list); break;
+ case 4: len += isp1301_dump(buf + len, name, " %08x", list); break;
+ }
+ return len;
+}
+
+char *isp1301_otg_control[8] = {
+ "DP_PULLUP",
+ "DM_PULLUP",
+ "DP_PULLDOWN",
+ "DM_PULLDOWN",
+ "ID_PULLDOWN",
+ "VBUS_DRV",
+ "VBUS_DISCHRG",
+ "VBUS_CHRG",
+};
+char *isp1301_interrupt_source[8] = {
+ "VBUS_VLD",
+ "SESS_VLD",
+ "DP_HI",
+ "ID_GND",
+ "DM_HI",
+ "ID_FLOAT",
+ "BDIS_ACON",
+ "CR_INT",
+};
+
+char *isp1301_mode_control_1[8] = {
+ "SPEED_REG",
+ "SUSPEND_REG",
+ "DAT_SE0",
+ "TRANSP_EN",
+ "BDIS_ACON_EN",
+ "OE_INT_EN",
+ "UART_EN",
+ NULL,
+};
+
+char *isp1301_mode_control_2[8] = {
+ "GLOBAL_PWR_ON",
+ "SPD_SUSP_CTRL",
+ "BI_DI",
+ "TRANSP_BDIR_0",
+ "TRANSP_BDIR_1",
+ "AUDIO_EN",
+ "PSW_OE",
+ "EN2V7",
+};
+
+char *isp1301_otg_status[8] = {
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "B_SESS_END",
+ "B_SESS_VLD",
+};
+
+/*! isp1301_detailed - dump detail information for reg port
+ * @param buf
+ * @param name
+ * @param reg
+ * @param detail
+ * @return
+ */
+int isp1301_detailed(char *buf, char *name, u8 reg, char **detail)
+{
+ u8 val;
+ int i;
+ int len = 0;
+
+ val = i2c_readb(reg);
+ for (i = 7; i >= 0; i--) {
+
+ if (3 == (i % 4))
+ len += sprintf (buf + len, "\n%-20s [%02d] ", name, reg);
+
+ if (detail[i])
+ len += sprintf (buf + len, "%14s%s ", detail[i], (val & (1 << i)) ? " " : "/");
+ else
+ len += sprintf (buf + len, "%14s%s ", "", " ");
+ }
+ len += sprintf (buf + len, "\n", name);
+
+ return len;
+}
+/*! isp1301_dump_all -
+ * @param buf
+ */
+int isp1301_dump_all(char *buf)
+{
+ int len = 0;
+ len += isp1301_dump_list(buf + len, "ISP1301 Standard", isp1301_reg_list);
+ switch (isp1301_private.transceiver_map->transceiver_type) {
+ case isp1301:
+ len += isp1301_dump_list(buf + len, "ISP1301 Extra", isp1301_spec_list);
+ break;
+ case max3301e:
+ len += isp1301_dump_list(buf + len, "MAX3301E Extra", max3301e_spec_list);
+ break;
+ default:
+ break;
+ }
+#ifdef CONFIG_ARCH_MX2ADS
+ len += isp1301_dump_list(buf + len, "MX21 ADS Extra", mx21_spec_list);
+#endif /* CONFIG_ARCH_MX2ADS */
+ return len;
+}
+/*! isp 1301_dump_detail
+ * @param buf
+ * @return buf length
+ */
+int isp1301_dump_detail(char *buf)
+{
+ int len = 0;
+
+ len += isp1301_detailed(buf + len, "MODE CONTROL 1", ISP1301_MODE_CONTROL_1, isp1301_mode_control_1);
+ len += isp1301_detailed(buf + len, "MODE CONTROL 2", ISP1301_MODE_CONTROL_2, isp1301_mode_control_2);
+ len += isp1301_detailed(buf + len, "INTERRUPT ENABLE", ISP1301_INTERRUPT_ENABLE_HIGH, isp1301_interrupt_source);
+
+ len += isp1301_detailed(buf + len, "OTG CONTROL", ISP1301_OTG_CONTROL_SET, isp1301_otg_control);
+ len += isp1301_detailed(buf + len, "INTERRUPT SOURCE", ISP1301_INTERRUPT_SOURCE, isp1301_interrupt_source);
+ len += isp1301_detailed(buf + len, "OTG STATUS", ISP1301_OTG_STATUS, isp1301_otg_status);
+ return len;
+}
+
+
+/*!
+ * isp1301_device_proc_read - implement proc file system read.
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return
+ * statistics for.
+ * @param file - file pointer
+ * @param buf - buffer pointer
+ * @param count -
+ * @param pos -
+ * @return read length
+ */
+static ssize_t isp1301_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ int index;
+ int i;
+ u32 r;
+
+ int config_size;
+
+ // get a page, max 4095 bytes of data...
+ //RETURN_EINVAL_UNLESS ((page = get_free_page (GFP_KERNEL)));
+ RETURN_EINVAL_UNLESS ((page = GET_KERNEL_PAGE()));
+
+ len = 0;
+ index = (*pos)++;
+
+ //printk(KERN_INFO"%s: index: %d\n", __FUNCTION__, index);
+ switch(index) {
+ case 0:
+ len += sprintf ((char *) page + len, "ISP1301 Transceiver Registers\n");
+
+ TRACE_MSG0(REMOVE_TCD, "UPDATING");
+ isp1301_update_all();
+ TRACE_MSG0(REMOVE_TCD, "UPDATE FINISHED");
+ len += sprintf ((char *) page + len , "Vendor: %04x Product: %04x Revision: %04x %s\n",
+ isp1301_private.vendor, isp1301_private.product,
+ isp1301_private.revision, isp1301_private.transceiver_map->name
+ );
+
+ len += isp1301_dump_all((char *) page + len);
+ len += sprintf ((char *) page + len, "\n");
+
+ break;
+
+ case 1:
+ len += sprintf ((char *) page + len, "\n--\n");
+ len += isp1301_dump_detail((char *) page + len);
+ len += sprintf ((char *) page + len, "\n");
+ break;
+
+ default:
+ break;
+
+ }
+
+ //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len);
+
+ if (len > count)
+ len = -EINVAL;
+
+ else if ((len > 0) && copy_to_user (buf, (char *) page, len))
+ len = -EFAULT;
+
+ //printk(KERN_INFO"%s: len: %d\n", __FUNCTION__, len);
+ free_page (page);
+ return len;
+}
+
+static struct file_operations isp1301_device_proc_operations_functions = {
+ read:isp1301_device_proc_read_functions,
+};
+
+/* Module init ******************************************************************************* */
+/*! isp1301_procfs_init - create a proc entry for isp1301 procfs
+ */
+int isp1301_procfs_init (void)
+{
+ struct proc_dir_entry *p;
+
+ // create proc filesystem entries
+ if ((p = create_proc_entry ("isp1301", 0, 0)) == NULL)
+ return -ENOMEM;
+
+ p->proc_fops = &isp1301_device_proc_operations_functions;
+
+ isp1301_update_all();
+
+ return 0;
+}
+void isp1301_procfs_exit (void)
+{
+ remove_proc_entry ("isp1301", NULL);
+}
+#else
+int isp1301_procfs_init (void) { return 0;
+}
+void isp1301_procfs_exit (void) { }
+#endif
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/hardware/isp1301.c b/drivers/otg/hardware/isp1301.c
new file mode 100644
index 000000000000..1994ac849bb2
--- /dev/null
+++ b/drivers/otg/hardware/isp1301.c
@@ -0,0 +1,1159 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/isp1301.c -- USB Transceiver Controller driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/isp1301/isp1301.c|20070612233038|28967
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/isp1301.c
+ * @brief ISP1301 OTG Transceiver Driver.
+ *
+ * This is the generic ISP1301 TCD core support.
+ *
+ * Notes:
+ *
+ * 1. The ISP1301 can control the speed and suspend directly, would it be
+ * appropriate to allow state machine to control this.
+ *
+ * 2. The ISP1301 has auto connect feature, can this be used without change
+ * to state machine.
+ *
+ * 3. The ISP1301 can control the ADR/PSW pin to enable / disable external
+ * charge pump.
+ *
+ * @ingroup ISP1301TCD
+ */
+
+#include <otg/otg-compat.h>
+
+#if defined(CONFIG_OTG_ISP1301) || defined(_OTG_DOXYGEN)
+
+//#include <asm/irq.h>
+//#include <asm/system.h>
+//#include <asm/io.h>
+//#include <linux/i2c.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+//#include <otg/otg-task.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-ocd.h>
+#include <otg/otg-pcd.h>
+
+//#include <linux/i2c.h>
+
+#include "isp1301-hardware.h"
+#include "isp1301.h"
+
+
+//#ifdef CONFIG_OMAP_H2
+//#include <asm/arch/gpio.h>
+//#include <asm/arch/mux.h>
+//#endif /* CONFIG_OMAP_H2 */
+
+/*! @var struct otg_transceiver_map isp1301_transceiver_map
+ */
+struct otg_transceiver_map isp1301_transceiver_map[] = {
+ { isp1301, 0x4cc, 0x1301, 0x00, "Phillips ISP1301", },
+ { max3301e, 0x6a0b, 0x0133, 0x00, "Maxim MAX3301E", },
+ { 0, 0, 0, 0, "Unknown Transceiver", },
+};
+
+struct isp1301_private isp1301_private;
+struct i2c_client *omap_i2c_client;
+//static struct file *i2c_file;
+#define MAX_I2C 16
+
+
+
+/* ********************************************************************************************* */
+#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r))
+#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r))
+#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r))
+#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r))
+
+
+
+/*! isp1301_int_src - update interrupt source test mask
+ *
+ * This sets the current mask and updates the interrupt registers to match.
+ * @param otg - otg_instance pointer
+ * @param int_src -
+ */
+void isp1301_int_src(struct otg_instance *otg, u8 int_src)
+{
+ TRACE_MSG1(otg->tcd->TAG, "setting int_src %02x", int_src);
+ isp1301_private.int_src = int_src;
+ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, ~int_src);
+ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, ~int_src);
+ i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_SET, int_src);
+ i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_SET, int_src);
+ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_src);
+}
+
+/*! isp1301_int_src_set - add to the interrupt source mask
+ * @param otg - otg_instance pointer
+ * @param int_src - interrupt maks to add
+ */
+void isp1301_int_src_set(struct otg_instance *otg, u8 int_src)
+{
+ isp1301_int_src(otg, int_src |= isp1301_private.int_src);
+}
+
+/*! isp1301_int_src_clr - remove from the interrup source mask
+ * @param otg - otg_instance pointer
+ * @param int_src - interrupt mask to remove
+ */
+void isp1301_int_src_clr(struct otg_instance *otg, u8 int_src)
+{
+ isp1301_int_src(otg, int_src = isp1301_private.int_src & ~int_src);
+}
+
+/* ********************************************************************************************* */
+
+/*!create a type for struct struct isp1301_test
+ * @brief typedef struct isp1301_test isp1301_test_t
+*/
+typedef struct isp1301_test {
+ u32 input;
+ char *name;
+ u8 mask;
+} isp1301_test_t;
+
+#define OV(i, m) {i, #i, m}
+
+/*! struct isp1301_test isp1301_int_src_tests */
+
+struct isp1301_test isp1301_int_src_tests[9] = {
+
+ OV(VBUS_VLD, ISP1301_VBUS_VLD),
+ OV(A_SESS_VLD, ISP1301_SESS_VLD),
+ OV(DP_HIGH, ISP1301_DP_HI),
+ OV(ID_GND, ISP1301_ID_GND),
+ OV(DM_HIGH, ISP1301_DM_HI),
+ OV(ID_FLOAT, ISP1301_ID_FLOAT),
+ OV(BDIS_ACON, ISP1301_BDIS_ACON),
+ OV(CR_INT_DET, ISP1301_CR_INT),
+ {0, NULL, 0},
+};
+
+/*! struct isp1301_test isp1301_otg_status-tests */
+struct isp1301_test isp1301_otg_status_tests[3] = {
+
+ OV(B_SESS_END, ISP1301_B_SESS_END),
+ OV(B_SESS_VLD, ISP1301_B_SESS_VLD),
+ {0, NULL, 0},
+};
+
+/*! isp1301_event() - set input bit based on current settings and interrupt mask
+ * @param otg - otg_instance pointer
+ * @param tests -
+ * @param val
+ * @param testmask
+ * @param msg
+ * @return inputs mask value
+ */
+otg_current_t isp1301_event(struct otg_instance *otg, struct isp1301_test *tests, int val, int testmask, char *msg)
+{
+ int i;
+ otg_current_t inputs = 0;
+ TRACE_MSG3(otg->tcd->TAG, "val: %02x mask: %02x %s", val, testmask, msg);
+ for (i = 0; tests[i].mask; i++) {
+ otg_current_t input = tests[i].input;
+ u8 mask = tests[i].mask;
+ CONTINUE_UNLESS(mask & testmask);
+ inputs |= (val & mask) ? input : _NOT(input);
+ TRACE_MSG6(otg->tcd->TAG, "%s%s %02x %08x inputs: %08x %08x",
+ tests[i].name, (val & mask) ? "" : "/", tests[i].mask, tests[i].input,
+ (u32) (inputs >> 32), (u32)(inputs & 0xffffffff)
+ );
+ }
+ return inputs;
+}
+
+
+
+int isp1301_bh_first;
+int isp1301_debounce;
+otg_tick_t isp1301_ticks;
+
+void isp1301_info(char *msg, u8 value)
+{
+ #if 0
+ otg_tick_t new_ticks = otg_tmr_ticks();
+ otg_tick_t ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks);
+
+ if (ticks < 10000)
+ TRACE_MSG3(PCD, "ISP1301 %d uS %s: %02x", (u32)(ticks & 0xffffffff), msg, value);
+ else if (ticks < 10000000)
+ TRACE_MSG3(PCD, "ISP1301 %d mS %s: %02x", (u32)((ticks >> 10) & 0xffffffff), msg, value);
+ else
+ TRACE_MSG3(PCD, "ISP1301 %d S %s: %02x", (u32)((ticks >> 20) & 0xffffffff), msg, value);
+
+ isp1301_ticks = new_ticks;
+ #endif
+}
+
+
+void isp1301_trace(u8 int_src, u8 int_changed, char *msg)
+{
+
+ #if 0
+ otg_tick_t new_ticks = otg_tmr_ticks();
+ otg_tick_t ticks = otg_tmr_elapsed(&new_ticks, &isp1301_ticks);
+
+ if (ticks < 10000)
+ TRACE_MSG4(PCD, "ISP1301 %d uS %s: src: %02x chng: %02x",
+ (u32)(ticks & 0xffffffff), msg, int_src, int_changed);
+ else if (ticks < 10000000)
+ TRACE_MSG4(PCD, "ISP1301 %d mS %s src: %02x chng: %02x",
+ (u32)((ticks >> 10) & 0xffffffff), msg, int_src, int_changed);
+ else
+ TRACE_MSG4(PCD, "ISP1301 %d S %s src: %02x chng: %02x ",
+ (u32)((ticks >> 20) & 0xffffffff), msg, int_src, int_changed);
+
+ isp1301_ticks = new_ticks;
+ #endif
+}
+
+#if 0
+/*! isp1301_bh
+ */
+void *isp1301_bh(void *data)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+
+ u8 otg_status = 0, special_function_22 = 0;
+ u8 int_lat, int_src1 = 0, int_src_prev = 0;
+ #if defined(CONFIG_MX2_TO11)
+ u8 int_src2 = 0;
+ #endif /* defined(CONFIG_MX2_TO11) */
+ static u8 otg_status_saved, special_function_22_saved;
+ static u8 int_src_saved;
+
+ int count = 0;
+
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG0(otg->tcd->TAG, "--");
+
+ /*if isp1301_private is NOT filled before then return */
+ RETURN_NULL_UNLESS (isp1301_private.ready);
+
+ do {
+ otg_current_t inputs = 0;
+
+ printk(KERN_INFO"%s: loop\n", __FUNCTION__);
+
+ /* read the interrupt latch and and clear latch and update otg
+ */
+ if ((int_lat = i2c_readb(ISP1301_INTERRUPT_LATCH_SET))) {
+ //TRACE_MSG1(TCD, "CLEARING INT LAT: %02x", int_lat);
+ isp1301_info("INT LAT", int_lat);
+ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_lat);
+ }
+
+ isp1301_info("INT_LAT", int_lat);
+
+ /* If int_lat shows a change or we are forcing an update, read interrupt source.
+ * Note that we mask DP_HI and DM_HI when LOC_CONN is set, we don't want them
+ * when USB Bus is in use.
+ *
+ * Mask with bits we are currently interested in.
+ *
+ * Ensure that we have two matching reads to ensure that value is stable.
+ * Also ensure that interrupt source is non-zero.
+ */
+
+ int_src1 = i2c_readb(ISP1301_INTERRUPT_SOURCE);
+
+ printk(KERN_INFO"%s: int_src: %02x %02x", __FUNCTION__, int_src_saved, int_src1);
+
+ BREAK_IF(int_src1 == int_src_prev);
+ int_src_prev = int_src1;
+
+ isp1301_trace(int_src1, int_src_saved ^ int_src1, "INT_SRC_1");
+
+ #if defined(CONFIG_MX2_TO11)
+ do {
+ int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE); // re-read interrupt source
+ BREAK_IF(int_src1 && (int_src1 & (ISP1301_ID_GND | ISP1301_ID_FLOAT)) &&
+ (int_src1 == int_src2)); // finished if non-zero and same
+ isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2");
+ int_src1 = int_src2; // save most recent read value
+ } while (1);
+ #endif /* defined(CONFIG_MX2_TO11) */
+
+
+
+ #if 0
+ while ( (int_src1 != (int_src2 = i2c_readb(ISP1301_INTERRUPT_SOURCE) & isp1301_private.int_src))) {
+ isp1301_trace(int_src2, int_src_saved ^ int_src2, "INT_SRC_2");
+ int_src1 = int_src2;
+ }
+ #endif
+
+ if (isp1301_bh_first || int_src_saved ^ (int_src1 & isp1301_private.int_src)) {
+ int_src_saved = int_src1;
+ inputs |= isp1301_event(otg, isp1301_int_src_tests, int_src1,
+ isp1301_private.int_src, "ISP1301 INT SRC");
+ if (int_src1 & ISP1301_VBUS_VLD) {
+ TRACE_MSG0(otg->tcd->TAG, "VBUS_VLD, setting B_SESS_VLD");
+ inputs |= B_SESS_VLD;
+ }
+ else if (!(int_src1 & ISP1301_SESS_VLD)) {
+ TRACE_MSG0(otg->tcd->TAG, "A_SESS_VLD/, setting B_SESS_VLD/");
+ inputs |= B_SESS_VLD_;
+ }
+ }
+
+ #if 1
+ /* further work if B-Device
+ */
+ if ( !(int_src1 & ISP1301_ID_GND) || !(int_src_saved & ISP1301_ID_GND) ) {
+ /* read the otg_status register and update otg state machine
+ *
+ * XXX this needs to be conditional on Transceiver type. The
+ * MAX3301E for example supports sess_end in a separate register.
+ */
+ switch (isp1301_private.transceiver_map->transceiver_type) {
+ case isp1301:
+ otg_status = i2c_readb(ISP1301_OTG_STATUS);
+ isp1301_trace(otg_status, int_src_saved ^ int_src1, "OTG_STATUS");
+ TRACE_MSG3(otg->tcd->TAG, "otg_status_saved: %02x otg_status: %02x chng: %02x",
+ otg_status_saved, otg_status, otg_status_saved ^ otg_status);
+
+ if (isp1301_bh_first || (otg_status_saved ^ otg_status)) {
+ inputs |= isp1301_event(otg, isp1301_otg_status_tests, otg_status, 0xff,
+ "ISP1301 OTG STATUS");
+ otg_status_saved = otg_status;
+ }
+
+ #if 0
+ isp1301_info("OTG STATUS", otg_status);
+ if (isp1301_bh_first)
+ otg_status_saved = ~otg_status;
+
+ otg_status_saved = isp1301_update_otg_status( otg, otg_status,
+ otg_status_saved ^ otg_status);
+ #endif
+ break;
+
+ // XXX
+ case max3301e:
+ special_function_22 = i2c_readb(MAX3301E_SPECIAL_FUNCTION_2_SET);
+ break;
+ case unknown_transceiver:
+ break;
+ }
+ }
+ #endif
+ if (inputs) {
+ u32 reset = (u32)(inputs >> 32);
+ u32 set = (u32) (inputs & 0xffffffff);
+ TRACE_MSG3(otg->tcd->TAG, "ISP1301: RESET: %08x SET: %08x %s", reset, set, "QUEUE");
+ otg_event(otg, inputs, otg->tcd->TAG, "ISP1301");
+ }
+ isp1301_bh_first = 0;
+
+ } while ((int_lat & isp1301_private.int_src) && (count++ < 6));
+ TRACE_MSG1(otg->tcd->TAG, "count: %d", count);
+
+ otg->tcd->vbus = BOOLEAN(int_src_saved & ISP1301_VBUS_VLD);
+ otg->tcd->id = BOOLEAN(int_src_saved & ISP1301_ID_GND);
+ printk(KERN_INFO"%s: finis\n", __FUNCTION__);
+ return NULL;
+}
+
+#else
+/*! isp1301_bh -
+ * @param data - otg_instance pointer
+ */
+
+void *isp1301_bh(void *data)
+{
+ u8 int_lat, int_src1 = 0, otg_status = 0;
+ otg_current_t inputs = 0;
+
+ struct otg_instance *otg = (struct otg_instance *) data;
+
+ TRACE_MSG0(otg->tcd->TAG, "--");
+
+ int_lat = i2c_readb(ISP1301_INTERRUPT_LATCH_SET);
+ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_lat);
+
+ int_src1 = i2c_readb(ISP1301_INTERRUPT_SOURCE);
+// printk(KERN_INFO"%s: interrupt source: %x latch: %x\n", __FUNCTION__, int_src1, int_lat);
+ inputs |= isp1301_event(otg, isp1301_int_src_tests, int_src1,
+ isp1301_private.int_src, "ISP1301 INT SRC");
+ if (int_src1 & ISP1301_VBUS_VLD) {
+ TRACE_MSG0(otg->tcd->TAG, "VBUS_VLD, setting B_SESS_VLD");
+ inputs |= B_SESS_VLD;
+ }
+ else if (!(int_src1 & ISP1301_SESS_VLD)) {
+ TRACE_MSG0(otg->tcd->TAG, "A_SESS_VLD/, setting B_SESS_VLD/");
+ inputs |= B_SESS_VLD_;
+ }
+
+ //Extra work for otg
+ otg_status = i2c_readb(ISP1301_OTG_STATUS);
+ inputs |= isp1301_event(otg, isp1301_otg_status_tests, otg_status, 0xff, "ISP1301 OTG STATUS");
+ //End of extra work for otg
+
+
+ if (inputs) {
+ //u32 reset = (u32)(inputs >> 32);
+ //u32 set = (u32) (inputs & 0xffffffff);
+ TRACE_MSG2(otg->tcd->TAG, "inputs: %x otg->task: %x\n", inputs, otg->task);
+ if (otg->task)
+ otg_event(otg, inputs, otg->tcd->TAG, "ISP1301");
+ }
+ otg->tcd->vbus = BOOLEAN(int_src1 & ISP1301_VBUS_VLD);
+ otg->tcd->id = BOOLEAN(int_src1 & ISP1301_ID_GND);
+ return NULL;
+}
+#endif
+
+/*! isp1301_work - process isp1301 task work
+ * @param data - otg_instance pointer
+ */
+void *isp1301_work(otg_task_arg_t data)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301 WORK AWAKE");
+ return isp1301_private.work_proc(data);
+}
+
+/*! isp1301_bh_wakeup - wakeup the isp1301 bottom half
+ * @param otg - otg_instance pointer
+ * @param first
+ */
+void isp1301_bh_wakeup(struct otg_instance *otg, int first)
+{
+ TRACE_MSG1(otg->tcd->TAG, "ISP1301 WAKEUP%s", first ? " FIRST" : "");
+ isp1301_bh_first |= first;
+ otg_up_work(isp1301_private.task);
+}
+
+
+/* ********************************************************************************************** */
+#if 0
+/*! isp1301_vbus - Do we have Vbus (cable attached?)
+ * Return non-zero if Vbus is detected.
+ *
+ */
+int isp1301_vbus (struct otg_instance *otg)
+{
+ return isp1301_private.int_src & ISP1301_VBUS_VLD ? 1 : 0;
+}
+
+/*! isp1301_id - Do we have Vbus (cable attached?)
+ * Return non-zero if Vbus is detected.
+ *
+ */
+int isp1301_id (struct otg_instance *otg)
+{
+ return isp1301_private.int_src & ISP1301_ID_GND ? 1 : 0;
+}
+#endif
+
+/* ********************************************************************************************* */
+/*! isp1301_tcd_en() - used to enable
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ */
+void isp1301_tcd_init(struct otg_instance *otg, u8 flag)
+{
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "SET");
+ break;
+ case PULSE:
+ TRACE_MSG0(otg->tcd->TAG, "PULSE");
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "RESET");
+ break;
+ }
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ isp1301_bh_wakeup(otg, TRUE);
+ otg_event(otg, OCD_OK, otg->tcd->TAG, "ISP1301 OK");
+}
+
+/*! isp1301_tcd_en() - used to enable
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ */
+void isp1301_tcd_en(struct otg_instance *otg, u8 flag)
+{
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "SET");
+ break;
+ case PULSE:
+ TRACE_MSG0(otg->tcd->TAG, "PULSE");
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "RESET");
+ break;
+ }
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ isp1301_bh_wakeup(otg, TRUE);
+}
+
+
+/*! isp1301_chrg_vbus - used to enable or disable B-device Vbus charging
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_chrg_vbus(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "CHRG_VBUS_SET");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_CHRG);
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "CHRG_VBUS_RESET");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET);
+ break;
+ case PULSE:
+ break;
+ }
+}
+
+/*! isp1301_drv_vbus - used to enable or disable A-device driving Vbus
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_drv_vbus(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "DRV_VBUS_SET");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DRV);
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "DRV_VBUS_RESET");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET);
+ break;
+ }
+}
+
+/*! isp1301_dischrg_vbus - used to enable Vbus discharge
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dischrg_vbus(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_SET");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG);
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_RESET");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET);
+ break;
+ }
+}
+
+/*! isp1301_mx21_vbus_drain - used to enable Vbus discharge
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_SET");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_VBUS_DISCHRG);
+ break;
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_RESET");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_VBUS_RESET);
+ break;
+ }
+}
+
+/*! isp1301_dp_pullup_func - used to enable or disable peripheral connecting to bus
+ *
+ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5
+ *
+ * host peripheral
+ * d+ pull-up clr set
+ * d+ pull-down set clr
+ *
+ * d- pull-up clr clr
+ * d- pull-down set set
+ *
+ */
+/*! isp1301_dp_pullup_func
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dp_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ isp1301_private.flags |= ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN SET - Set DP PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLUP);
+ break;
+
+ case RESET:
+ isp1301_private.flags &= ~ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN RESET - Clr DP PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLUP);
+ break;
+ }
+}
+
+/*! isp1301_dm_pullup_func - used to enable or disable peripheral connecting to bus
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dm_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ isp1301_private.flags |= ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN SET - Set DM PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLUP);
+ break;
+
+ case RESET:
+ isp1301_private.flags &= ~ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN RESET - Clr DM PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLUP);
+ break;
+ }
+}
+
+/*! isp1301_dp_pulldown_func - used to enable or disable peripheral connecting to bus
+ *
+ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5
+ *
+ * host peripheral
+ * d+ pull-up clr set
+ * d+ pull-down set clr
+ *
+ * d- pull-up clr clr
+ * d- pull-down set set
+ *
+ */
+/*! isp1301_dp_pulldown_func -
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dp_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ isp1301_private.flags |= ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN SET - Set DP PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN);
+ break;
+
+ case RESET:
+ isp1301_private.flags &= ~ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN RESET - Clr DP PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN);
+ break;
+ }
+}
+
+/*! isp1301_dm_pulldown_func - used to enable or disable peripheral connecting to bus
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dm_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ isp1301_private.flags |= ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN SET - Set DM PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN);
+ break;
+
+ case RESET:
+ isp1301_private.flags &= ~ISP1301_LOC_CONN;
+ TRACE_MSG0(otg->tcd->TAG, "ISP1301_LOC_CONN RESET - Clr DM PULLUP");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DM_PULLDOWN);
+ break;
+ }
+}
+
+/*! isp1301_peripheral_host_func - used to enable or disable peripheral connecting to bus
+ *
+ * A-Device D+ pulldown D- pulldown
+ * idle set set
+ * host set set
+ * peripheral reset set
+ *
+ * B-Device
+ * idle set set
+ * host set set
+ * peripheral reset set
+ */
+void isp1301_peripheral_host_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET: // peripheral
+ TRACE_MSG0(otg->tcd->TAG, "SET - CLR DP PULLDOWN");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLDOWN);
+ break;
+
+ case RESET: // host
+ TRACE_MSG0(otg->tcd->TAG, "RESET - SET DM PULLDOWN");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN);
+ break;
+ }
+}
+
+/*! isp1301_dm_det_func - used to enable or disable D- detect
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dm_det_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting DM_HI detect");
+ isp1301_int_src_set(otg, ISP1301_DM_HI);
+ isp1301_bh_wakeup(otg, TRUE);
+ isp1301_debounce = 0;
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting DM_HI detect");
+ isp1301_int_src_clr(otg, ISP1301_DM_HI);
+ isp1301_bh_wakeup(otg, TRUE);
+ isp1301_debounce = 1;
+ break;
+ }
+}
+
+/*! isp1301_dp_det_func - used to enable or disable D+ detect
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_dp_det_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting DP_HI detect");
+ isp1301_int_src_set(otg, ISP1301_DP_HI);
+ isp1301_bh_wakeup(otg, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting DP_HI detect");
+ isp1301_int_src_clr(otg, ISP1301_DP_HI);
+ isp1301_bh_wakeup(otg, TRUE);
+ break;
+ }
+}
+
+/*! isp1301_cr_det_func - used to enable or disable D+ detect
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_cr_det_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting CR_INT detect");
+ isp1301_int_src_set(otg, ISP1301_CR_INT);
+ isp1301_bh_wakeup(otg, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting CR_INT detect");
+ isp1301_int_src_clr(otg, ISP1301_CR_INT);
+ isp1301_bh_wakeup(otg, TRUE);
+ break;
+ }
+}
+
+/*! isp1301_bdis_acon_func - used to enable or disable auto a-connect
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_bdis_acon_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting BDIS ACON");
+ isp1301_int_src_set(otg, ISP1301_BDIS_ACON);
+ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_BDIS_ACON_EN);
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting BDIS ACON");
+ i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_BDIS_ACON_EN);
+ isp1301_int_src_clr(otg, ISP1301_BDIS_ACON);
+ break;
+ }
+}
+
+/*! isp1301_id_pulldown_func - used to enable or disable ID pulldown
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_id_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting ID PULLDOWN");
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_ID_PULLDOWN);
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting ID PULLDOWN");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_ID_PULLDOWN);
+ break;
+ }
+}
+
+/*! isp1301_audio_func - used to enable or disable Carkit Interrupt
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_audio_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "SET AUDIO_EN");
+ //isp1301_int_src_set(otg, ISP1301_CR_INT);
+ i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_AUDIO_EN);
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "RESET AUDIO_EN");
+ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_AUDIO_EN);
+ //isp1301_int_src_clr(otg, ISP1301_CR_INT);
+ break;
+ }
+}
+
+/*! isp1301_uart_func - used to enable or disable transparent uart mode
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_uart_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting UART_EN");
+ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_UART_EN);
+ /* XXX enable uart */
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting UART_EN");
+ i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_UART_EN);
+ /* XXX disable uart */
+ break;
+ }
+}
+
+/*! isp1301_mono_func - used to enable or disable mono audio connection
+ * @param otg - otg_instance pointer
+ * @param flag -
+ */
+void isp1301_mono_func(struct otg_instance *otg, u8 flag)
+{
+ //TRACE_MSG0(otg->tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->tcd->TAG, "setting MONO");
+ /* XXX enable mono output */
+ break;
+
+ case RESET:
+ TRACE_MSG0(otg->tcd->TAG, "reseting MONO");
+ /* XXX disable mono output */
+ break;
+ }
+}
+
+
+/* ********************************************************************************************* */
+#ifdef CONFIG_OTG_ISP1301_PROCFS
+extern int isp1301_procfs_init (void);
+extern void isp1301_procfs_exit (void);
+#endif /* CONFIG_OTG_ISP1301_PROCFS */
+
+/*! isp1301_mod_init
+ * @param otg - otg_instance pointer
+ * @param work_proc - task process function
+ */
+int isp1301_mod_init(struct otg_instance *otg, otg_task_proc_t work_proc)
+{
+ /* Setup work item, use isp1301_bh directly if platform is not
+ * providing a bottom half wrapper.
+ */
+ TRACE_MSG0(otg->tcd->TAG, "1. setup work item");
+
+ isp1301_private.work_proc = work_proc ? work_proc : &isp1301_bh;
+
+ RETURN_EINVAL_UNLESS(( isp1301_private.task = otg_task_init2("isp1301", isp1301_work, otg, otg->tcd->TAG)));
+ otg->task = isp1301_private.task;
+// isp1301_private.task->debug = TRUE;
+ otg_task_start(isp1301_private.task);
+ return 0;
+}
+
+/*! isp1301_mod_exit
+ */
+void isp1301_mod_exit(struct otg_instance *otg)
+{
+#ifdef CONFIG_OTG_ISP1301_PROCFS
+ isp1301_procfs_exit ();
+#endif /* CONFIG_OTG_ISP1301_PROCFS */
+ otg_task_exit(isp1301_private.task);
+}
+
+/* ********************************************************************************************* */
+/*! isp1301_configure - configure the ISP1301 for this host
+ * @param otg - otg instance
+ * @param tx_mode - the type of connection between the host USB and the ISP1301
+ * @param spd_ctrl - suspend control method
+ */
+void isp1301_configure(struct otg_instance *otg, isp1301_tx_mode_t tx_mode, isp1301_spd_ctrl_t spd_ctrl)
+{
+ //u16 vendor = 0, product = 0, revision = 0;
+ //u16 vendor = 0, product = 0, revision = 0;
+ //u8 mode1, mode2, control;
+ u8 mode1, mode2;
+
+ struct otg_transceiver_map *map = isp1301_transceiver_map;
+
+
+#if defined(CONFIG_OTG_ISP1301_MX2ADS) || defined(CONFIG_OTG_ISP1301_MX2ADS_MODULE)
+ u32 vp;
+ TRACE_MSG0(otg->tcd->TAG, "1. Read Transceiver ID's with long read");
+ vp = i2c_readl(ISP1301_VENDOR_ID);
+ isp1301_private.vendor = vp & 0xffff;
+ isp1301_private.product = vp >> 16;
+ isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID);
+ mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET);
+ mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET);
+
+ TRACE_MSG5(otg->tcd->TAG, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x",
+ isp1301_private.vendor, isp1301_private.product, isp1301_private.revision, mode1, mode2);
+
+#else /* defined(CONFIG_OTG_ISP1301_MX2ADS) */
+
+ isp1301_private.vendor = i2c_readw(ISP1301_VENDOR_ID);
+ isp1301_private.product = i2c_readw(ISP1301_PRODUCT_ID);
+ isp1301_private.revision = i2c_readw(ISP1301_VERSION_ID);
+ mode1 = i2c_readb(ISP1301_MODE_CONTROL_1_SET);
+ mode2 = i2c_readb(ISP1301_MODE_CONTROL_2_SET);
+
+ TRACE_MSG5(otg->tcd->TAG, "2. OTG Transceiver: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x",
+ isp1301_private.vendor, isp1301_private.product, isp1301_private.revision, mode1, mode2);
+
+#endif /* defined(CONFIG_OTG_ISP1301_MX2ADS) */
+
+
+ printk(KERN_INFO"%s: vendor: %04x product: %04x revision: %04x mode1: %02x mode2: %02x\n", __FUNCTION__,
+ isp1301_private.vendor, isp1301_private.product, isp1301_private.revision, mode1, mode2
+ );
+
+ for ( ; map && map->transceiver_type != unknown_transceiver; map++) {
+ CONTINUE_UNLESS ((isp1301_private.vendor == map->vendor) && (isp1301_private.product == map->product));
+ TRACE_MSG1(otg->tcd->TAG, "Found Transceiver: %s", map->name);
+ isp1301_private.transceiver_map = map;
+ break;
+ }
+ UNLESS (isp1301_private.transceiver_map)
+ isp1301_private.transceiver_map = isp1301_transceiver_map;
+
+ TRACE_MSG0(otg->tcd->TAG, "3. Disable All Transceiver Control Register 1 ");
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, 0xff); // clear
+
+ TRACE_MSG0(otg->tcd->TAG, "4. Enable D-/D+ Pulldowns in Transceiver Control Register 1 ");
+
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DM_PULLDOWN | ISP1301_DP_PULLDOWN);
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, (u8) ISP1301_DM_PULLUP | ISP1301_DP_PULLUP);
+
+
+ TRACE_MSG0(otg->tcd->TAG, "5. Clear latch and enable interrupts");
+ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, 0xff);
+ //i2c_writeb(ISP1301_INTERRUPT_ENABLE_LOW_CLR, 0xeb);
+ //i2c_writeb(ISP1301_INTERRUPT_ENABLE_HIGH_CLR, 0xeb);
+ isp1301_int_src_set(otg, 0xeb);
+
+ /* The PSW_OE enables the ADR/PSW pin for output driving either high
+ * or low depending on the address.
+ *
+ * ADR ADR_REG Address PSW_OE=0 PSW_OE=1
+ *
+ * low 0 0x2c LOW HIGH
+ *
+ * high 1 0x2d HIGH LOW
+ *
+ *
+ * The i2c address by tying the ADR/PSW pin either high or low.
+ * Setting the PSW_OE drives the ADR/PSW into the opposite of the
+ * default wiring.
+ *
+ * On the MX21ADS the ADR/PSW pin is wired high which enables the
+ * charge pump. So enabling the PSW_OE is required to disable Vbus
+ * generation on the Charge Pump.
+ *
+ * The MAX3355E will only enable Vbus when this signal is high AND
+ * the ID signal is low. So it may be safe to leave enabled when
+ * operating as a B-device (ID floating.)
+ */
+
+ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low
+
+
+ TRACE_MSG1(otg->tcd->TAG, "6. tx_mode: %02x", tx_mode);
+ /*
+ * DAT_SE0
+ * 0 - VP_VM mode
+ * 1 - DAT_SE0 mode
+ */
+ switch (tx_mode) {
+ case vp_vm_unidirectional:
+ case vp_vm_bidirectional:
+ TRACE_MSG0(otg->tcd->TAG, "6. tx_mode: VP_VM");
+ i2c_writeb(ISP1301_MODE_CONTROL_1_CLR, ISP1301_DAT_SE0);
+ break;
+
+ case dat_se0_unidirectional:
+ case dat_se0_bidirectional:
+ TRACE_MSG0(otg->tcd->TAG, "6. tx_mode: DAT_SE0");
+ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_DAT_SE0);
+ break;
+ }
+ /*
+ * BI_DI
+ * 0 - unidirectional
+ * 1 - bidirectional
+ */
+ switch (tx_mode) {
+ case vp_vm_unidirectional:
+ case dat_se0_unidirectional:
+ TRACE_MSG0(otg->tcd->TAG, "6. tx_mode: unidirectional");
+ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_BI_DI);
+ break;
+ case vp_vm_bidirectional:
+ case dat_se0_bidirectional:
+ TRACE_MSG0(otg->tcd->TAG, "6. tx_mode: bidirectional");
+ i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_BI_DI);
+ break;
+ }
+ TRACE_MSG0(otg->tcd->TAG, "6. tx_mode done");
+
+ TRACE_MSG1(otg->tcd->TAG, "7. spd_ctrl: %02x", spd_ctrl);
+ switch (spd_ctrl) {
+ case spd_susp_pins:
+ i2c_writeb(ISP1301_MODE_CONTROL_2_CLR, ISP1301_SPD_SUSP_CTRL);
+ break;
+ case spd_susp_reg:
+ i2c_writeb(ISP1301_MODE_CONTROL_1_SET, ISP1301_SPEED_REG);
+ i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_SPD_SUSP_CTRL);
+ break;
+ }
+
+ //i2c_writeb(ISP1301_MODE_CONTROL_2_SET, ISP1301_PSW_OE); // PSW_OE - OFFVBUS low
+
+ TRACE_MSG0(otg->tcd->TAG, "7. spd_ctrl done");
+
+ TRACE_MSG1(otg->tcd->TAG, "8. transceiver_type: %02x", isp1301_private.transceiver_map);
+
+ TRACE_MSG1(otg->tcd->TAG, "8. transceiver_type: %02x", isp1301_private.transceiver_map->transceiver_type);
+ switch (isp1301_private.transceiver_map->transceiver_type) {
+ case isp1301:
+ break;
+ case max3301e:
+ i2c_writeb(MAX3301E_SPECIAL_FUNCTION_1_SET, MAX3301E_SESS_END);
+ break;
+ case unknown_transceiver:
+ break;
+ }
+
+ TRACE_MSG0(otg->tcd->TAG, "8. transceiver_type done");
+
+ TRACE_MSG0(otg->tcd->TAG, "9. enable interrupts");
+ isp1301_int_src(otg, ISP1301_ID_FLOAT | ISP1301_DM_HI | ISP1301_ID_GND | ISP1301_DP_HI | ISP1301_SESS_VLD | ISP1301_VBUS_VLD);
+
+#ifdef CONFIG_OTG_ISP1301_PROCFS
+ TRACE_MSG0(otg->tcd->TAG, "3.");
+ isp1301_procfs_init ();
+#endif /* CONFIG_OTG_ISP1301_PROCFS */
+
+ /* Set the ready flag in isp1301_private to show that the structure is ready for use */
+ isp1301_private.ready = TRUE;
+
+#if 0 //For debugging
+
+ printk(KERN_INFO"%s: VENDOR_ID %04x\n", __FUNCTION__, i2c_readb(ISP1301_VENDOR_ID));;
+ printk(KERN_INFO"%s: PRODUCT_ID %04x\n", __FUNCTION__, i2c_readb(ISP1301_PRODUCT_ID));;
+ printk(KERN_INFO"%s: VERSION_ID %04x\n", __FUNCTION__, i2c_readb(ISP1301_VERSION_ID));;
+
+ printk(KERN_INFO"%s: OTG_CONTROL_SET %02x\n", __FUNCTION__, i2c_readb(ISP1301_OTG_CONTROL_SET));;
+ printk(KERN_INFO"%s: INTERRUPT_SOURCE %02x\n", __FUNCTION__, i2c_readb(ISP1301_INTERRUPT_SOURCE));;
+ printk(KERN_INFO"%s: INTERRUPT_LATCH_SET %02x\n", __FUNCTION__, i2c_readb(ISP1301_INTERRUPT_LATCH_SET));;
+ printk(KERN_INFO"%s: INTERRUPT_ENABLE_LOW %02x\n", __FUNCTION__, i2c_readb(ISP1301_INTERRUPT_ENABLE_LOW));;
+ printk(KERN_INFO"%s: INTERRUPT_ENABLE_HIGH %02x\n", __FUNCTION__, i2c_readb(ISP1301_INTERRUPT_ENABLE_HIGH));;
+ printk(KERN_INFO"%s: MOD_CONTROL_1 %02x\n", __FUNCTION__, i2c_readb(ISP1301_MODE_CONTROL_1));;
+ printk(KERN_INFO"%s: MOD_CONTROL_2 %02x\n", __FUNCTION__, i2c_readb(ISP1301_MODE_CONTROL_2));;
+#endif
+}
+
+void isp1301_exit(void)
+{
+ i2c_writeb_direct(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLDOWN | ISP1301_DM_PULLDOWN);
+ i2c_writeb_direct(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLUP | ISP1301_DM_PULLUP);
+
+}
+
+#endif /* defined(CONFIG_OTG_ISP1301) */
diff --git a/drivers/otg/hardware/isp1301.h b/drivers/otg/hardware/isp1301.h
new file mode 100644
index 000000000000..3beebb2786d8
--- /dev/null
+++ b/drivers/otg/hardware/isp1301.h
@@ -0,0 +1,239 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/isp1301.h -- USB Transceiver Controller driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/isp1301/isp1301.h|20061218212925|39953
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @defgroup ISP1301TCD Philips ISP1301
+ * @ingroup TCD
+ */
+/*!
+ * @file otg/hardware/isp1301.h
+ * @brief Private structures and defines for ISP1301 Transciever Controller Driver.
+ *
+ * This file contains the private defines and structures for the ISP1301 Transceiver
+ * Driver.
+ *
+ * @ingroup ISP1301TCD
+ */
+
+/*!
+ * @name ISP1301 ISP1301 configuration
+ * @{
+ */
+
+#ifdef CONFIG_OMAP_H2
+#define ISP1301_GPIO (2)
+#define TCD_ISP1301_I2C_ADDR ISP1301_I2C_ADDR_HIGH;
+#endif
+
+#define ISP1301_NAME "isp1301"
+
+#define ISP1301_LOC_CONN 0x01
+
+#define ISP1301_INT_SRC 0xeb
+
+/*!
+ * @enum otg_transceiver
+ * Define ISP1301 compatible transceivers
+ */
+typedef enum otg_transceiver {
+ unknown_transceiver, /*!< unknown */
+ isp1301, /*!< Philips ISP1301 */
+ max3301e /*!< Maxim MAX3301e */
+} otg_transceiver_t;
+
+/*!
+ * @struct otg_transceiver_map
+ * How to determine transceiver model and manufacturer
+ */
+struct otg_transceiver_map {
+ otg_transceiver_t transceiver_type; /*!< transceiver type */
+ u16 vendor; /*!< vendor number */
+ u16 product; /*!< product number */
+ u16 revision; /*!< revision number */
+ char *name; /*!< name */
+};
+
+/*!
+ * @struct isp1301_private
+ * Information required for operation
+ */
+struct isp1301_private {
+ struct otg_task *task;
+ otg_task_proc_t work_proc;
+ //void (*work_proc) (void *);
+
+ u16 vendor; /*!< vendor number found */
+ u16 product; /*!< product number found */
+ u16 revision; /*!< revision number found */
+ u32 flags; /*!< flags */
+ u8 int_src; /*!< current interrupt source */
+ struct otg_transceiver_map *transceiver_map; /*! pointer to transceiver map for this transceiver */
+ BOOL ready; /*!< it is set when the structure is ready and filled */
+};
+
+/*!
+ * @var typedef enum isp1301_tx_mode isp1301_tx_mode_t
+ * This defines the various ways that the ISP1301 can be
+ * wired into the host USB.
+ *
+ *
+ * VP_VM unidirectional mode
+ *
+ * VP_VM bidirectional mode
+ *
+ *
+ * DAT_SE0 unidirectional mode
+ *
+ * DAT_SE0 bidirectional mode
+ *
+ *
+ *
+ *
+ * C.f. Figure 23 OTG controller with DAT_SE0 SIE interface
+ * 3-Wire - DAT_SE0 Bi-Directional - Single ended (Dat, SE0)
+ * __
+ * TXEN ---- OE ---> OE (9)
+ *
+ * RXD/TXD <---- TXDAT/RCVDAT ---> DAT (14 DAT/VP)
+ *
+ * SE0 <---- TXSE0/RCVSE0 ---> SE0 (13 SE0/VM)
+ * ______
+ * GPIO <---- OTGIRQ --- INT_N (5) Active low
+ *
+ *
+ * C.f. Figure 24 OTG controller with VP_VM SIE interface
+ * 4-Wire - VP_VM Bi-directional
+ *
+ * __
+ * TXEN ---- OE ---> OE (9)
+ *
+ * VP <---- TXVP/RCVVP ---> VP (14 DAT/VP)
+ *
+ * VM <---- TXVM/RCVVM ---> VM (10)
+ *
+ * RCV <---- RCV --- RCV (12)
+ * ______
+ * GPIO <---- OTGIRQ --- INT_N (5) Active low
+ *
+ *
+ *
+ *
+ * 6-Wire VP_VM Uni-directional
+ * __
+ * TXEN ---- OE ---> OE (9)
+ *
+ * TXD ---- TXDAT ---> VP (14 DAT/VP)
+ *
+ * SE0 ---- TXSE0 ---> VM (13 SE0/VM)
+ *
+ * RCV <---- RCV --- RCV (12)
+ *
+ * RCVVP <---- RCVVP --- VP (11)
+ *
+ * RCVVM <---- RCVVM --- VM (10)
+ * ______
+ * GPIO <---- OTGIRQ --- INT_N (5) Active low
+ *
+ *
+ *
+ */
+typedef enum isp1301_tx_mode {
+ dat_se0_bidirectional, /*!< 3-Wire Single-Ended (DAT, SEO) / Single-Ended (DAT, SE0) */
+ vp_vm_bidirectional, /*!< 4-Wire Differential (TxDp,TxDM) / Single-Ended (DAT, SE0)*/
+ vp_vm_unidirectional, /*!< 6-Wire Differential (TxDp,TxDM) / Differential (RxDp, RxDM, RxD )*/
+ dat_se0_unidirectional, /*!< 6-Wire Single-Ended (DAT, SE0) / Differential (RxDp, RxDM, RxD ) */
+} isp1301_tx_mode_t;
+
+/*!
+ * @var typedef enum isp1301_spd_ctrl isp1301_spd_ctrl_t
+ * This defines how the speed and suspend pins are controlled
+ * for this host.
+ */
+typedef enum isp1301_spd_ctrl {
+ spd_susp_pins, /*!< controlled by SPEED and SUSPEND pins */
+ spd_susp_reg, /*!< controled by SPEED_REG and SUSPEND_REG in Mode Control 1 register */
+} isp1301_spd_ctrl_t;
+
+
+extern struct tcd_ops tcd_ops;
+extern struct isp1301_private isp1301_private;
+extern struct tcd_instance *tcd_instance;
+
+/* isp1301.c */
+extern int isp1301_mod_init(struct otg_instance *, otg_task_proc_t );
+//extern int isp1301_mod_init__(struct otg_instance *);
+extern void isp1301_mod_exit(struct otg_instance *);
+extern void isp1301_tcd_init(struct otg_instance *, u8 );
+extern void isp1301_tcd_en(struct otg_instance *, u8 );
+extern void isp1301_chrg_vbus(struct otg_instance *, u8 );
+extern void isp1301_drv_vbus(struct otg_instance *, u8 );
+extern void isp1301_dischrg_vbus(struct otg_instance *, u8 );
+extern void isp1301_dp_pullup_func(struct otg_instance *, u8 );
+extern void isp1301_dm_pullup_func(struct otg_instance *, u8 );
+extern void isp1301_dp_pulldown_func(struct otg_instance *, u8 );
+extern void isp1301_dm_pulldown_func(struct otg_instance *, u8 );
+extern void isp1301_peripheral_host_func(struct otg_instance *, u8 );
+extern void isp1301_dm_det_func(struct otg_instance *, u8 );
+extern void isp1301_dp_det_func(struct otg_instance *, u8 );
+extern void isp1301_cr_det_func(struct otg_instance *, u8 );
+extern void isp1301_bdis_acon_func(struct otg_instance *, u8 );
+extern void isp1301_mx21_vbus_drain_func(struct otg_instance *, u8 );
+extern void isp1301_id_pulldown_func(struct otg_instance *, u8 );
+extern void isp1301_audio_func(struct otg_instance *, u8 );
+extern void isp1301_uart_func(struct otg_instance *, u8 );
+extern void isp1301_mono_func(struct otg_instance *, u8 );
+
+extern void isp1301_otg_timer(struct otg_instance *, u8 );
+extern void *isp1301_bh(void *);
+extern int isp1301_id (struct otg_instance *);
+extern int isp1301_vbus (struct otg_instance *);
+extern void isp1301_configure(struct otg_instance *, isp1301_tx_mode_t, isp1301_spd_ctrl_t );
+
+extern void isp1301_bh_wakeup(struct otg_instance *, int);
+
+/* thread */
+//extern int isp1301_thread_init (void (bh_proc)(void *));
+//extern void isp1301_thread_exit (wait_queue_head_t *);
+//extern void isp1301_thread_wakeup(int enabled, int first);
+
+
+
+/* i2c-xxx.c */
+int i2c_configure(char *name, int addr);
+extern void i2c_close(void);
+extern u8 i2c_readb(u8 );
+extern u16 i2c_readw(u8 );
+extern u32 i2c_readl(u8 );
+extern void i2c_writeb(u8 , u8 );
+extern void i2c_writeb_direct(u8 , u8 );
+//extern void i2c_write(u8 , u8 );
+
+
+/* ********************************************************************************************* */
+#define TRACE_I2CB(t,r) TRACE_MSG3(t, "%-40s[%02x] %02x", #r, r, i2c_readb(r))
+#define TRACE_I2CW(t,r) TRACE_MSG3(t, "%-40s[%02x] %04x", #r, r, i2c_readw(r))
+#define TRACE_GPIO(t,b,r) TRACE_MSG2(t, "%-40s %04x", #r, readw(b + r))
+#define TRACE_REGL(t,r) TRACE_MSG2(t, "%-40s %08x", #r, readl(r))
+
+/* @} */
diff --git a/drivers/otg/hardware/l26-ocd-dev.c b/drivers/otg/hardware/l26-ocd-dev.c
new file mode 100644
index 000000000000..22eecbd7832b
--- /dev/null
+++ b/drivers/otg/hardware/l26-ocd-dev.c
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/l26-ocd-dev.c -- Generic Linux 2.6 timer Dev driver wrapper
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/otglib/l26-ocd-dev.c|20070612232808|61728
+ *
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/l26-ocd-dev.c
+ * @brief Generic L26 OCD Driver.
+ *
+ * OTG-DEV wrapper for l26-ocd.c
+ *
+ * @ingroup LINUXOS
+ * @ingroup OTGDEV
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+/* Other includes*/
+#include <linux/poll.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-dev.h>
+#include <otg/otg-ocd.h>
+
+extern struct ocd_ops l26_ocd_ops;
+
+/* ********************************************************************************************* */
+
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+void l26_ocd_remove(struct otg_dev *otg_dev);
+int l26_ocd_probe(struct otg_dev *otg_dev);
+
+static void
+l26_ocd_dev_remove(struct otg_dev *otg_dev)
+{
+ l26_ocd_remove(otg_dev);
+}
+
+static int
+l26_ocd_dev_probe(struct otg_dev *otg_dev)
+{
+ return l26_ocd_probe(otg_dev);
+}
+
+#endif /* CONFIG_HIGH_RES_TIMERS */
+
+/* ********************************************************************************************* */
+static struct otg_dev_driver arc_ocd_driver = {
+ .name = "l26-ocd-dev",
+ .id = OTG_DRIVER_OCD,
+ #ifdef CONFIG_HIGH_RES_TIMERS
+ .probe = l26_ocd_dev_probe,
+ .remove = l26_ocd_dev_remove,
+ #endif /* CONFIG_HIGH_RES_TIMERS */
+ .ops = &l26_ocd_ops,
+};
+
+/* ********************************************************************************************* */
+
+/*! l26_ocd_dev_module_init() - module init
+ */
+int l26_ocd_dev_module_init (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return otg_dev_register_driver(otg_device_driver, &arc_ocd_driver);
+}
+
+/*!
+ * l26_ocd_dev_module_exit() - module exit
+ */
+void l26_ocd_dev_module_exit (struct otg_device_driver *otg_device_driver)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ otg_dev_unregister_driver (otg_device_driver, &arc_ocd_driver);
+}
diff --git a/drivers/otg/hardware/l26-ocd.c b/drivers/otg/hardware/l26-ocd.c
new file mode 100644
index 000000000000..fdfd9b1b2259
--- /dev/null
+++ b/drivers/otg/hardware/l26-ocd.c
@@ -0,0 +1,256 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/l26-ocd.c -- Generic Linux 2.6 Timer
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/otglib/l26-ocd.c|20070612232808|58041
+ *
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/l26-ocd.c
+ * @brief Generic L26 OCD Driver.
+ *
+ * Provides ticks via gettimeofday() and optional a OTG timer
+ * using high resolution timer.
+ *
+ * CONFIG_HIGH_RES_TIMERS=y
+ *
+ * Optionally provides accurate OTG timer using Linux High
+ * Resolution Timer.
+ *
+ * CONFIG_HIGH_RES_TIMERS=n
+ *
+ * Start timer is a dummy if High Resolution Timers not
+ * enabled.
+ *
+ * @ingroup LINUXOS
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+/* Other includes*/
+#include <linux/pci.h>
+#include <linux/poll.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/dma.h>
+//#include <asm/hrtime.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-dev.h>
+#include <otg/otg-api.h>
+#include <otg/otg-ocd.h>
+
+/* ********************************************************************************************* */
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+
+struct l26_hr_timer {
+ struct timer_list timer_list;
+ BOOL active;
+ u32 usec_set;
+ u32 jiffy_per_sec;
+};
+
+/*!
+ * l26_hrt_callback() - called to queue an a timer event to otg queue
+ * @param arg - pointer of otg_instance type
+ */
+
+void l26_hrt_callback (unsigned long arg)
+{
+ struct ocd_instance *ocd = (struct ocd_instance *) arg;
+ struct otg_instance *otg = ocd->otg;
+ struct l26_hr_timer *hr_timer = ocd->privdata;
+
+ RETURN_UNLESS(hr_timer->active);
+ hr_timer->active = FALSE;
+ //TRACE_MSG3 (ocd->TAG, "usec: %d expires: %8u arch_cycle_expires: %8u",
+ // hr_timer->usec_set, hr_timer.expires, hr_timer.arch_cycle_expires);
+
+ otg_queue_event(otg, TMOUT, ocd->TAG, "HRT TMOUT");
+}
+
+
+#if 0
+/*!
+ * l26_hrt_start_timer() - start a timer for otg state machine
+ * Set or reset timer to interrupt in number of uS (micro-seconds).
+ *
+ * XXX There may be a floor or minimum that can be effectively set.
+ * XXX We have seen an occasional problem with US(25) for discharge for example.
+ *
+ * @param otg
+ * @param usec
+ * @return 0 on success
+ */
+int l26_hrt_start_timer(struct otg_instance *otg, int usec)
+{
+ struct ocd_instance *ocd = otg->ocd;
+ struct l26_hr_timer *hr_timer = ocd->privdata;
+
+ TRACE_MSG1(ocd->TAG, "usec: %d", usec);
+
+ hr_timer->usec_set = usec;
+ //TRACE_MSG1 (otg->ocd->TAG, "usec: %d", usec);
+
+ hr_timer->active = FALSE;
+ TRACE_MSG1(ocd->TAG, "resetting active: %d", hr_timer->active);
+
+ del_timer(&hr_timer->timer_list);
+ RETURN_ZERO_UNLESS(usec);
+
+ hr_timer->active = TRUE;
+ TRACE_MSG1(ocd->TAG, "setting active: %d", hr_timer->active);
+
+ if (hr_timer->usec_set >= 1000000) {
+ hr_timer->timer_list.expires = jiffies + ((hr_timer->usec_set/1000000)*hr_timer->jiffy_per_sec);
+ hr_timer->timer_list.arch_cycle_expires = get_arch_cycles(jiffies);
+ TRACE_MSG4 (otg->ocd->TAG, "usec: %u jiffies: %8u expires: %8u arch_cycle_expires: %8u LONG",
+ usec, jiffies, hr_timer->timer_list.expires, hr_timer->timer_list.arch_cycle_expires);
+ }
+ else {
+ hr_timer->timer_list.expires = jiffies;
+ hr_timer->timer_list.arch_cycle_expires = get_arch_cycles(jiffies);
+
+ if (hr_timer->usec_set < 100) {
+ TRACE_MSG1(otg->ocd->TAG, "usec: %d set to minimum 100", hr_timer->usec_set);
+ hr_timer->usec_set = 100;
+ }
+ hr_timer->timer_list.arch_cycle_expires += nsec_to_arch_cycle(hr_timer->usec_set * 1000);
+
+ //TRACE_MSG2(ocd->TAG, "arch_cycle_expires: %d arch_cycles_per_jiffy: %d",
+ // hr_timer->timer_list.arch_cycle_expires, hr_timer->timer_list.arch_cycles_per_jiffy);
+
+ while (hr_timer->timer_list.arch_cycle_expires >= arch_cycles_per_jiffy) {
+ hr_timer->timer_list.expires++;
+ hr_timer->timer_list.arch_cycle_expires -= arch_cycles_per_jiffy;
+ }
+ TRACE_MSG4 (ocd->TAG, "usec: %u jiffies: %8u expires: %8u arch_cycle_expires: %8u SHORT",
+ usec, jiffies, hr_timer->timer_list.expires, hr_timer->timer_list.arch_cycle_expires);
+ }
+
+ add_timer(&hr_timer->timer_list);
+ return 0;
+}
+#endif
+#endif /* CONFIG_HIGH_RES_TIMERS */
+/* ********************************************************************************************* */
+/*!
+ * l26_ocd_start_timer() - start a timer for otg state machine
+ * Set or reset timer to interrupt in number of uS (micro-seconds).
+ *
+ * @param otg
+ * @param usec
+ */
+int l26_ocd_start_timer(struct otg_instance *otg, int usec)
+{
+ otg_event(otg, TMOUT, otg->ocd->TAG, "l26 Timer - FAKE TMOUT");
+ return 0;
+}
+
+/* l26_ocd_ticks - get current ticks
+ */
+otg_tick_t l26_ocd_ticks (void)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ #if 0
+ return tv.tv_sec * 1000000 + tv.tv_usec;
+ #else
+ return tv.tv_sec << 20 | (tv.tv_usec & 0xfffff);
+ #endif
+}
+
+/* l26_ocd_elapsed - return micro-seconds between two tick values
+ */
+otg_tick_t l26_ocd_elapsed(otg_tick_t *t1, otg_tick_t *t2)
+{
+ #if 0
+ return (((t1 > t2) ? (t1 - t2) : (t2 - *t1)));
+ #else
+ otg_tick_t n1 = (*t1 >> 20) * 1000000 + (*t1 & 0xfffff);
+ otg_tick_t n2 = (*t1 >> 20) * 1000000 + (*t2 & 0xfffff);
+ return (((n1 > n2) ? (n1 - n2) : (n2 - n1)));
+ #endif
+}
+
+
+struct ocd_ops l26_ocd_ops = {
+ #ifdef xxxx_CONFIG_HIGH_RES_TIMERS
+ .start_timer = l26_hrt_start_timer,
+ #else /* CONFIG_HIGH_RES_TIMERS */
+ .start_timer = l26_ocd_start_timer,
+ #endif /* CONFIG_HIGH_RES_TIMERS */
+ .ticks = l26_ocd_ticks,
+ .elapsed = l26_ocd_elapsed,
+};
+
+/* ********************************************************************************************* */
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+void
+l26_ocd_remove(struct otg_dev *otg_dev)
+{
+ struct ocd_instance *ocd = otg_dev->ocd_instance;
+ RETURN_UNLESS (ocd->privdata);
+ LKFREE(ocd->privdata);
+ ocd->privdata = NULL;
+}
+
+int
+l26_ocd_probe(struct otg_dev *otg_dev)
+{
+ struct ocd_instance *ocd = otg_dev->ocd_instance;
+ struct l26_timer *hr_timer = NULL;
+ RETURN_EINVAL_UNLESS((ocd->privdata = CKMALLOC(sizeof(struct l26_hr_timer))));
+ #if 0
+ init_timer (hr_timer);
+ hr_timer->timer_list.expires = jiffies + 10;
+ hr_timer->timer_list.function = l26_hrt_callback;
+ hr_timer->timer_list.data = (unsigned long) ocd_instance;
+ hr_timer->timer_list.arch_cycle_expires = get_arch_cycles(jiffies);
+ hr_timer->timer_list.arch_cycle_expires += nsec_to_arch_cycle(100 * 1000 * 1000);
+ while (hr_timer->timer_list.arch_cycle_expires >= arch_cycles_per_jiffy) {
+ hr_timer->timer_list.expires++;
+ hr_timer->timer_list.arch_cycle_expires -= arch_cycles_per_jiffy;
+ }
+ #endif
+ return 0;
+}
+#endif /* CONFIG_HIGH_RES_TIMERS */
diff --git a/drivers/otg/hardware/mxc-gpio.c b/drivers/otg/hardware/mxc-gpio.c
new file mode 100644
index 000000000000..93fbaef850b5
--- /dev/null
+++ b/drivers/otg/hardware/mxc-gpio.c
@@ -0,0 +1,276 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-gpio.c - Freescale USBOTG common gpio and iomux setting
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/mxc/mxc-gpio.c|20070612233038|29528
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc-gpio.c
+ * @brief Freescale USB Host Controller Driver
+ *
+ * @ingroup FSOTG
+ */
+
+#include <otg/pcd-include.h>
+
+#ifdef CONFIG_OTG_ZASEVB_ATLAS_CONNECTIVITY
+#include <asm/arch/gpio.h>
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+#include <asm/arch/gpio.h>
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#if defined(CONFIG_MACH_ARGONPLUSEVB) || defined(CONFIG_ARCH_ARGONPLUSEVB) || defined(CONFIG_MACH_ARGONPLUSODYSSEY) || defined(CONFIG_ARCH_ARGONLV)
+#define ZGPIO_PORT 0
+#define ZGPIO_PIN 2
+#endif/* defined(CONFIG_MACH_ARGONPLUSEVB)*/
+
+#if defined(CONFIG_MACH_SCMA11EVB) || defined(CONFIG_ARCH_SCMA11EVB)
+#define ZGPIO_PORT 2
+#define ZGPIO_PIN 12
+#endif /* CONFIG_MACH_SCMA11EVB */
+
+#ifdef CONFIG_ARCH_ZEUS
+#define ZGPIO_PORT 1
+#define ZGPIO_PIN 30
+#endif /* CONFIG_ARCH_ZEUS */
+
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301) || defined(_OTG_DOXYGEN)
+
+BOOL zasevb_int_disabled = FALSE;
+
+/* ********************************************************************************************* */
+/*!
+ * zasevb_gpio_int_hndlr() - gpio interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ * @return interrupt handler status
+ * This disables the gpio interrup and schedules the isp1301 bottom half handler.
+ *
+ */
+static irqreturn_t zasevb_gpio_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ gpio_config_int_en(ZGPIO_PORT, ZGPIO_PIN, FALSE);
+ zasevb_int_disabled = TRUE;
+
+ TRACE_MSG0(TCD, "ZASEVB GPIO INTERRUPT: SCHEDULE WORK");
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ isp1301_bh_wakeup(FALSE);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * zasevb_isp1301_bh()- call isp1301 bottom half handler
+ * @param arg
+ * This is a wrapper to the isp1301 bottom half handler, it
+ * re-enables the gpio interrupt after processing complete.
+*/
+void *zasevb_isp1301_bh(void *arg)
+{
+ TRACE_MSG0(TCD, "ZASEVB GPIO INTERRUPT: ISP1301_BH");
+ isp1301_bh(arg);
+ TRACE_MSG0(TCD, "ZASEVB GPIO INTERRUPT: REENABLE");
+ if (zasevb_int_disabled) {
+ zasevb_int_disabled = FALSE;
+ gpio_config_int_en(ZGPIO_PORT, ZGPIO_PIN, TRUE);
+ }
+ return NULL;
+}
+
+
+
+
+/*!
+ * This function set iomux settings depends on platform and usb mode.
+ *
+ * @param otg otg instance
+ * @param usb_mode setting usb mode.
+ *
+ */
+
+int mxc_iomux_gpio_isp1301_set (struct otg_instance *otg, int usb_mode)
+{
+
+ int gpio = 1;
+
+ printk (KERN_INFO"MXC gpio setting for isp1301\n");
+
+ isp1301_mod_init(otg, &zasevb_isp1301_bh);
+
+ TRACE_MSG0(TCD, "5. IOMUX and GPIO Interrupt Configuration");
+
+ #ifdef CONFIG_MACH_SCMA11EVB
+ iomux_config_mux(AP_GPIO_AP_C12, OUTPUTCONFIG_DEFAULT, INPUTCONFIG_NONE); // XXX INPUTCONFIG_DEFAULT?
+ #endif /* CONFIG_MACH_SCMA11EVB */
+
+ #ifdef CONFIG_MACH_ARGONPLUSEVB
+ iomux_config_mux(PIN_GPIO2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ #endif /* CONFIG_MACH_ARGONPLUSEVB */
+
+ #ifdef CONFIG_MACH_ZEUSEVB
+ iomux_config_mux(SPI2_SS1_PIN, MUX0_OUT, GPIO_MUX1_IN);
+ #endif /* CONFIG_MACH_ZEUSEVB */
+
+ //Settung interrupt for ISP1301
+ gpio_config(ZGPIO_PORT, ZGPIO_PIN, false, GPIO_INT_FALL_EDGE);
+ gpio = gpio_request_irq(ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO, zasevb_gpio_int_hndlr,
+ SA_SHIRQ, "ISP1301", (void *) &ocd_ops);
+ THROW_IF(gpio, error);
+ gpio_config_int_en(ZGPIO_PORT, ZGPIO_PIN, TRUE); // XXX this might not be needed
+
+
+ #if defined(CONFIG_ARCH_SCMA11)
+ iomux_config_mux(SP_USB_TXOE_B, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_DAT_VP, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_SE0_VM, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_RXD, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ #if 0
+ switch(hwmode) {
+ case XCVR_SE0_D_NEW:
+ case XCVR_D_SE0_NEW:
+ iomux_config_mux(AP_GPIO_AP_C16,OUTPUTCONFIG_FUNC3, INPUTCONFIG_FUNC3);
+ iomux_config_mux(AP_GPIO_AP_C17,OUTPUTCONFIG_FUNC3, INPUTCONFIG_FUNC3);
+ printk(KERN_INFO"%s: EXTRA IOMUX\n", __FUNCTION__);
+ break;
+
+ case XCVR_D_D:
+ case XCVR_SE0_SE0:
+ printk(KERN_INFO"%s: NO EXTRA IOMUX\n", __FUNCTION__);
+ break;
+ }
+ #endif
+ #endif /* CONFIG_ARCH_SCMA11 */
+
+ #if defined(CONFIG_ARCH_ARGONPLUS) || defined(CONFIG_ARCH_ARGONLV)
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ #endif /* CONFIG_ARCH_ARGONPLUS */
+
+
+ #ifdef CONFIG_ARCH_ZEUS
+ iomux_config_mux(USB_DAT_VP_PIN, MUX0_OUT, MUX0_IN); // iomux_com_base + 0x00
+ iomux_config_mux(USB_SE0_VM_PIN, MUX0_OUT, MUX0_IN); // iomux_com_base + 0x01
+ iomux_config_mux(USB_TXOE_B_PIN, MUX0_OUT, MUX0_IN); // iomux_com_base + 0x09
+ iomux_config_mux(USB_RXD_PIN, MUX0_OUT, MUX0_IN); // iomux_com_base + 0x3c
+ #endif
+
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ UNLESS (gpio) gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*! mxc_iomux_gpio_isp1301_reset - reset isp1301 gpio irq setting
+ */
+int mxc_iomux_gpio_isp1301_reset (void)
+{
+ gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ return 0;
+}
+
+#endif //CONFIG_OTG_ZASEVB_ISP1301
+
+
+
+#ifdef CONFIG_OTG_ZASEVB_ATLAS_CONNECTIVITY
+/*! mxc_iomux_gpio_atlas_set - set atlas gpio settings
+ * @param usb_mode
+ * @return 0
+ */
+int mxc_iomux_gpio_atlas_set (int usb_mode)
+{
+
+ printk (KERN_INFO"MXC gpio setting for Atlas\n");
+ #if defined(CONFIG_ARCH_SCMA11)
+ printk(KERN_INFO"IOMUX setting for SCMA11\n");
+ iomux_config_mux(SP_USB_TXOE_B, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_DAT_VP, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_SE0_VM, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ iomux_config_mux(SP_USB_RXD, OUTPUTCONFIG_FUNC1, INPUTCONFIG_FUNC1);
+ #endif
+
+ #if defined(CONFIG_MACH_ARGONPLUSEVB) || defined(CONFIG_ARCH_ARGONPLUSEVB) || defined(CONFIG_MACH_ARGONPLUSODYSSEY) || defined(CONFIG_ARCH_ARGONLV)
+ printk(KERN_INFO"IOMUX setting for Argon+\n");
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ #endif /* CONFIG_ARCH_ARGONPLUS */
+
+
+ return 0;
+}
+
+/*! mxc_iomux_gpio_atlas_reset - reset atlas gpio settings
+ * @param usb_mode
+ * @return 0
+ */
+int mxc_iomux_gpio_atlas_reset (void)
+{
+
+ #if defined(CONFIG_ARCH_SCMA11)
+ iomux_config_mux(SP_USB_TXOE_B, OUTPUTCONFIG_FUNC2, INPUTCONFIG_FUNC2);
+ iomux_config_mux(SP_USB_DAT_VP, OUTPUTCONFIG_FUNC2, INPUTCONFIG_FUNC2);
+ iomux_config_mux(SP_USB_SE0_VM, OUTPUTCONFIG_FUNC2, INPUTCONFIG_FUNC2);
+ iomux_config_mux(SP_USB_RXD, OUTPUTCONFIG_FUNC2, INPUTCONFIG_FUNC2);
+ #endif
+
+ #if defined(CONFIG_MACH_ARGONPLUSEVB) || defined(CONFIG_ARCH_ARGONPLUSEVB)
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ #endif /* CONFIG_ARCH_ARGONPLUS */
+
+
+ return 0;
+}
+#endif //CONFIG_OTG_ZASEVB_ATLAS_CONNECTIVITY
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_reset);
+#endif
+#ifdef CONFIG_OTG_ZASEVB_ATLAS_CONNECTIVITY
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_atlas_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_atlas_reset);
+#endif
diff --git a/drivers/otg/hardware/mxc-gptcr.c b/drivers/otg/hardware/mxc-gptcr.c
new file mode 100644
index 000000000000..8de271e9b0ae
--- /dev/null
+++ b/drivers/otg/hardware/mxc-gptcr.c
@@ -0,0 +1,286 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-gptcr.c -- Freescale GPT timer
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/mxc/mxc-gptcr.c|20070612233038|11611
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/mxc-gptcr.c
+ * @brief Freecale GPT Timer implementation.
+ *
+ * The GPT2 timer is used for the OTG timer.
+ *
+ * The GPT3 timer is used as a free running counter for a source of ticks
+ * for use with the trace facility.
+ *
+ * @ingroup FSOTG
+ *
+ */
+
+
+#include <otg/pcd-include.h>
+
+#if defined (CONFIG_OTG_GPTR) || defined(_OTG_DOXYGEN)
+
+#include <linux/pci.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/board.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/preempt.h>
+
+
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+
+
+
+/* ********************************************************************************************* */
+
+
+static otg_tick_t mxc_gptcr_divisor = 0;
+static otg_tick_t mxc_gptcr_multiplier = 0;
+static int dev_id = 1;
+static bool mxc_shared_int = 0;
+
+/*!
+ * mxc_gptcr_ticks() - compute ticks from usecs
+ * @param usecs
+ * @return ticks
+ */
+otg_tick_t inline mxc_gptcr_ticks(u32 usecs)
+{
+ return (((otg_tick_t)usecs) * ((otg_tick_t)mxc_gptcr_divisor)) / ((otg_tick_t)mxc_gptcr_multiplier);
+}
+
+/*!
+ * mxc_gptcr_usecs() - compute usecs from ticks
+ * @param usecs
+ * @return usecs
+ */
+otg_tick_t inline mxc_gptcr_usecs(u32 usecs)
+{
+ return (((otg_tick_t)usecs) * ((otg_tick_t)mxc_gptcr_multiplier)) / ((otg_tick_t)mxc_gptcr_divisor);
+}
+
+/*!
+ * mxc_gptcr_trace_ticks() - get current ticks
+ * GPT3 is setup as free running clock at 266 Mhz - 1/266 = .0037
+ */
+otg_tick_t mxc_gptcr_trace_ticks (void)
+{
+ return (otg_tick_t) *_reg_GPT_GPTCNT;
+}
+
+/*!
+ * mxc_gptcr_trace_elapsed() - return micro-seconds between two tick values
+ *
+ * GPT3 is setup as free running clock at 266 Mhz - 1/266 = .003759
+ *
+ * MPLL = 266, FCLK = 266, BCLK = 88
+ *
+ */
+otg_tick_t mxc_gptcr_trace_elapsed(otg_tick_t *t1, otg_tick_t *t2)
+{
+ otg_tick_t ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1);
+// ticks = (ticks * mxc_gptcr_multiplier) / mxc_gptcr_divisor; // .3776
+ if (TICKS_PER_USEC != 0)
+ ticks /= TICKS_PER_USEC;
+ else
+ ticks = 0;
+ return ticks;
+}
+
+/* ********************************************************************************************* */
+//u32 mxc_gptcr_timeout;
+//u32 mxc_gptcr_ticks_start;
+
+u32 mxc_gptcr_usec_set;
+u32 mxc_gptcr_ticks_set;
+u32 mxc_gptcr_match_set;
+u32 mxc_gptcr_active;
+
+/*!
+ * mxc_gptcr_start_timer() - start a timer for otg state machine
+ * Set or reset timer to interrupt in number of uS (micro-seconds).
+ *
+ * XXX There may be a floor or minimum that can be effectively set.
+ * XXX We have seen an occasional problem with US(25) for discharge for example.
+ *
+ * @param otg
+ * @param usec
+ */
+int mxc_gptcr_start_timer(struct otg_instance *otg, int usec)
+{
+ u32 ticks;
+ u32 match;
+ unsigned long flags;
+ local_irq_save (flags);
+
+ //TRACE_MSG2(OCD, "usec: %d CNT: %08x", usec, *_reg_GPT_GPTCNT);
+
+ if (usec && (usec < 100)) {
+ usec = 100;
+ TRACE_MSG2(OCD, "usec: %d CNT: %08x", usec, *_reg_GPT_GPTCNT);
+ }
+
+ /*
+ * Disable Channel 3 compare.
+ */
+ *_reg_GPT_GPTCR &= ~(0x7 << 26);
+
+ mxc_gptcr_usec_set = usec;
+ if (usec) {
+
+ mxc_gptcr_ticks_set = ticks = (u32 ) mxc_gptcr_ticks(usec);
+ mxc_gptcr_match_set = 0;
+ mxc_gptcr_active = 0;
+
+
+ /*
+ * Compute and set match register
+ */
+ mxc_gptcr_match_set = match = *_reg_GPT_GPTCNT + ticks;
+ *_reg_GPT_GPTOCR3 = match;
+ mxc_gptcr_active = 1;
+
+ TRACE_MSG6(OCD, "cnt: %08x match: %08x GPTCNT: %08x GPTCR: %08x GPTSR: %08x GPTOCR3: %08x\n",
+ mxc_gptcr_ticks_set, mxc_gptcr_match_set,
+ *_reg_GPT_GPTCNT, *_reg_GPT_GPTCR, *_reg_GPT_GPTSR, *_reg_GPT_GPTOCR3);
+ /*
+ * Enable interrupt
+ */
+ *_reg_GPT_GPTIR |= (0x01 << 2);
+
+ }
+
+ local_irq_restore (flags);
+ return 0;
+}
+
+/*!
+ * mxc_gptcr_timer_int_hndlr() - timer interrupt
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t mxc_gptcr_timer_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 gptsr = *_reg_GPT_GPTSR;
+
+
+ *_reg_GPT_GPTIR &= ~(0x04);
+
+ if (gptsr & (0x01 << 2)){
+ TRACE_MSG7(OCD, "cnt: %08x match: %08x gptsr: %08x GPTCNT: %08x GPTCR: %08x GPTSR: %08x GPTOCR3: %08x\n",
+ mxc_gptcr_ticks_set, mxc_gptcr_match_set, gptsr,
+ *_reg_GPT_GPTCNT, *_reg_GPT_GPTCR, *_reg_GPT_GPTSR, *_reg_GPT_GPTOCR3);
+
+ *_reg_GPT_GPTCR &= ~(0x7 << 26);
+ *_reg_GPT_GPTOCR3 = 0;
+ TRACE_MSG0(OCD, "OCM3");
+ TRACE_MSG1(OCD, "active: %x", mxc_gptcr_active);
+ if (mxc_gptcr_active) {
+ TRACE_MSG0(OCD, "calling otg_event");
+ mxc_gptcr_active = 0;
+ otg_queue_event(ocd_instance->otg, TMOUT, OCD, "SCMA11 TMOUT");
+ }
+ else {
+ TRACE_MSG0(OCD, "skipping otg_event");
+ }
+ *_reg_GPT_GPTSR |= 0x4;
+ return IRQ_HANDLED;
+ }
+
+ if (!mxc_shared_int)
+ *_reg_GPT_GPTIR |= 0x4;
+
+ return IRQ_NONE;
+}
+
+
+/* ********************************************************************************************* */
+extern void fs_ocd_init(struct otg_instance *otg, u8 flag);
+
+
+
+/*!
+ * mxc_gptcr_ocd_mod_init() - initial tcd setup
+ * Allocate interrupts and setup hardware.
+ * @param divisor
+ * @param multiplier
+ */
+int mxc_gptcr_mod_init (int divisor, int multiplier)
+{
+ int timer;
+
+ if (*_reg_GPT_GPTCR & 0x1)
+ mxc_shared_int = 1;
+ else
+ mxc_shared_int = 0;
+
+ if (mxc_shared_int){
+ printk (KERN_INFO"Using shared interrupt for timer 3 \n");
+ timer = request_irq (INT_GPT, mxc_gptcr_timer_int_hndlr, SA_INTERRUPT | SA_SHIRQ,
+ UDC_NAME " OTG TIMER", (int *) &dev_id);
+ }
+ else{
+ printk (KERN_INFO"OTG TIMER is the only ISR for timer 3 \n");
+ timer = request_irq (INT_GPT, mxc_gptcr_timer_int_hndlr, SA_INTERRUPT | SA_SHIRQ,
+ UDC_NAME " OTG TIMER", (int *) &dev_id);
+ /* disable and reset GPT
+ */
+ *_reg_GPT_GPTCR &= ~0x00000001;
+ *_reg_GPT_GPTCR |= (1<<15);
+ while ((*_reg_GPT_GPTCR & (1<<15)) != 0) ;
+ *_reg_GPT_GPTPR = 0;
+ *_reg_GPT_GPTCR = (0x1 << 9) | (0x2 << 6);
+ *_reg_GPT_GPTCR |= 0x1;
+ }
+
+ TRACE_MSG0(OCD, "1. Interrupts requested");
+
+ THROW_IF(timer, error);
+
+ mxc_gptcr_divisor = divisor;
+ mxc_gptcr_multiplier = multiplier;
+
+ return 0;
+
+ CATCH(error) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * mxc_gptcr_mod_exit() - de-initialize
+ */
+void mxc_gptcr_mod_exit (void)
+{
+ mxc_gptcr_start_timer(NULL, 0);
+ free_irq (INT_GPT, (int *) &dev_id);
+}
+
+
+#endif //CONFIG_OTG_GPTR
diff --git a/drivers/otg/hardware/mxc-hardware.h b/drivers/otg/hardware/mxc-hardware.h
new file mode 100644
index 000000000000..dd923371068c
--- /dev/null
+++ b/drivers/otg/hardware/mxc-hardware.h
@@ -0,0 +1,816 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-hardware.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/mxc/mxc-hardware.h|20061218212926|59377
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @defgroup FSOTG Freescale MXC USBOTG Support
+ * @ingroup Hardware
+ */
+/*!
+ * @file otg/hardware/mxc-hardware.h
+ * @brief Hardware defines for Freescale USBOTG Hardware
+ *
+ * This supports the Freescale MXC implementation of the Trans Dimension
+ * USBOTG.
+ *
+ * This is used on:
+ *
+ * - i.MX21
+ * - SCM-A11
+ * - Argon+
+ * - Zeus
+ *
+ * @ingroup FSOTG
+ */
+
+
+/*!
+ * @name OTG_SYS_CTRL
+ * C.f. 23.8 USB Control Register
+ */
+ /*! @{ */
+
+//#define OTG_SYS_CTRL (OTG_SYS_BASE+0x00)
+
+#define SYS_CTRL_I2C_WU_INT_STAT (1 << 27)
+#define SYS_CTRL_OTG_WU_INT_STAT (1 << 26)
+#define SYS_CTRL_HOST_WU_INT_STAT (1 << 25)
+#define SYS_CTRL_FNT_WU_INT_STAT (1 << 24)
+
+#define SYS_CTRL_I2C_WU_INT_EN (1 << 19)
+#define SYS_CTRL_OTG_WU_INT_EN (1 << 18)
+#define SYS_CTRL_HOST_WU_INT_EN (1 << 17)
+#define SYS_CTRL_FNT_WU_INT_EN (1 << 16)
+
+#define SYS_CTRL_OTG_BYP_VAL (0x3 << 11)
+#define SYS_CTRL_HOST1_BYP_VAL (0x3 << 9)
+
+#define SYS_CTRL_OTG_PWR_MASK (1 << 6)
+#define SYS_CTRL_HOST1_PWR_MASK (1 << 6)
+#define SYS_CTRL_HOST2_PWR_MASK (1 << 4)
+#define SYS_CTRL_USB_BYP (1 << 2)
+#define SYS_CTRL_HOST1_TXEN_OE (1 << 1)
+/*! @} */
+
+/*!
+ * @name C.f. 23.9 USBOTG Module Registers
+ */
+ /*! @{ */
+
+#define OTG_CORE_HWMODE (OTG_CORE_BASE+0x00) // 32bit core hardware mode reg
+
+#define XCVR_D_D 0x00
+//#define XCVR_SE0_D_OLD 0x01 // XXX should be 0x02
+//#define XCVR_D_SE0_OLD 0x02 // XXX should be 0x01
+#define XCVR_SE0_D_NEW 0x02 // XXX should be 0x02
+#define XCVR_D_SE0_NEW 0x01 // XXX should be 0x01
+#define XCVR_SE0_SE0 0x03
+
+#define MODULE_ANASDBEN (1 << 14)
+#define MODULE_OTGXCVR (0x3 << 6)
+#define MODULE_HOSTXCVR (0x3 << 4)
+#define MODULE_CRECFG (0x3)
+#define MODULE_CRECFG_HHNP (0x0)
+#define MODULE_CRECFG_HOST (0x1)
+#define MODULE_CRECFG_FUNC (0x2)
+#define MODULE_CRECFG_SHNP (0x3)
+
+#define OTG_CORE_CINT_STAT (OTG_CORE_BASE+0x04) // 32bit core int status reg
+
+#define MODULE_FCINTDSPEN (1 << 6)
+
+#define MODULE_ASHNPINT (1 << 5)
+#define MODULE_ASFCINT (1 << 4)
+#define MODULE_ASHCINT (1 << 3)
+#define MODULE_HNPINT (1 << 2)
+#define MODULE_FCINT (1 << 1)
+#define MODULE_HCINT (1)
+
+#define OTG_CORE_CINT_STEN (OTG_CORE_BASE+0x08) // 32bit core int enable reg
+#define OTG_CORE_CINT_STEN_CLR (OTG_CORE_BASE+0x3c) // 32bit core int enable clear reg
+
+#define MODULE_ASHNPINT_EN (1 << 5)
+#define MODULE_ASFCINT_EN (1 << 4)
+#define MODULE_ASHCINT_EN (1 << 3)
+#define MODULE_HNPINT_EN (1 << 2)
+#define MODULE_FCINT_EN (1 << 1)
+#define MODULE_HCINT_EN (1)
+
+#define OTG_CORE_CLK_CTRL (OTG_CORE_BASE+0x0C) // 32bit core clock control reg
+
+#define MODULE_FUNC_CLK (1 << 2)
+#define MODULE_HOST_CLK (1 << 1)
+#define MODULE_MAIN_CLK (1)
+
+#define OTG_CORE_RST_CTRL (OTG_CORE_BASE+0x10) // 32bit core reset control reg
+
+#define MODULE_RSTI2C (1 << 15)
+#define MODULE_RSTCTRL (1 << 5)
+#define MODULE_RSTFC (1 << 4)
+#define MODULE_RSTFSIE (1 << 3)
+#define MODULE_RSTRH (1 << 2)
+#define MODULE_RSTHSIE (1 << 1)
+#define MODULE_RSTHC (1)
+
+#define OTG_CORE_FRM_INTVL (OTG_CORE_BASE+0x14) // 32bit core frame interval reg
+
+#define MODULE_RESET_FRAME (1 << 15)
+
+#define OTG_CORE_FRM_REMAIN (OTG_CORE_BASE+0x18) // 32bit core frame remaining reg
+
+#define OTG_CORE_HNP_CSTAT (OTG_CORE_BASE+0x1C) // 32bit core HNP current state reg
+
+#define MODULE_HNPDAT (1 << 30)
+#define MODULE_VBUSBSE (1 << 29)
+#define MODULE_VBUSABSV (1 << 28)
+#define MODULE_VBUSGTAVV (1 << 27)
+
+#define MODULE_ARMTHNPE (1 << 25)
+#define MODULE_BHNPEN (1 << 24)
+
+#define MODULE_SLAVE (1 << 22)
+#define MODULE_MASTER (1 << 21)
+#define MODULE_BGEN (1 << 20)
+#define MODULE_CMPEN (1 << 19)
+#define MODULE_ISBDEV (1 << 18)
+#define MODULE_ISADEV (1 << 17)
+
+#define MODULE_SWVBUSPUL (1 << 15)
+
+#define MODULE_SWAUTORST (1 << 12)
+#define MODULE_SWPUDP (1 << 11)
+
+#define MODULE_SWPDDM (1 << 9)
+#define MODULE_HNPSTATE (0x1f << 8)
+#define MODULE_CLRERROR (1 << 3)
+#define MODULE_ADROPBUS (1 << 2)
+#define MODULE_ABBUSREQ (1 << 1)
+
+#define HNP_A_IDLE (0x10 << 8)
+#define HNP_A_MASTER (0x11 << 8)
+#define HNP_A_SLAVE (0x12 << 8)
+#define HNP_A_WAIT_VPULSE (0x13 << 8)
+
+#define HNP_A_WAIT_DPULSE (0x14 << 8)
+#define HNP_A_WAIT_CONN_A (0x15 << 8)
+#define HNP_A_WAIT_CONN_B (0x16 << 8)
+#define HNP_A_WAIT_VRISE (0x17 << 8)
+#define HNP_A_SUSPEND (0x18 << 8)
+#define HNP_A_WAIT_VFALL (0x19 << 8)
+#define HNP_A_VBUS_ERR (0x1a << 8)
+#define HNP_CONN_DEBOUNCE (0x1b << 8)
+#define HNP_A_WAIT_ABREQ (0x1c << 8)
+#define HNP_B_IDLE (0x00 << 8)
+#define HNP_B_MASTER (0x01 << 8)
+#define HNP_B_SLAVE (0x02 << 8)
+#define HNP_B_SRP_INIT_V (0x03 << 8)
+#define HNP_B_SRP_INIT_D (0x04 << 8)
+#define HNP_B_PRE_WAIT_CONN_AB_SHORT_DB (0x05 << 8)
+#define HNP_B_WAIT_CONN_A (0x06 << 8)
+#define HNP_B_PRE_SLAVE (0x0a << 8)
+
+
+#define OTG_CORE_HNP_TIMER1 (OTG_CORE_BASE+0x20) // 32bit core HNP timer 1 reg
+#define OTG_CORE_HNP_TIMER2 (OTG_CORE_BASE+0x24) // 32bit core HNP timer 2 reg
+#define OTG_CORE_HNP_T3PCR (OTG_CORE_BASE+0x28) // 32bit core HNP timer 3 pulse ctrl
+
+#define HNP_DATAPULSE (1 << 5)
+#define HNP_VBUSPULSE (1 << 4)
+
+#define OTG_CORE_HINT_STAT (OTG_CORE_BASE+0x2C) // 32bit core HNP int status reg
+
+#define HNP_I2COTGINT (1 << 15)
+#define HNP_AWAITBTO (1 << 8) // Hardware HNP Only
+#define HNP_AIDLEBDTO (1 << 7) // Hardware HNP Only
+#define HNP_SRPSUCFAIL (1 << 6) // Hardware HNP Only
+#define HNP_SRPINT (1 << 5)
+#define HNP_VBUSERROR (1 << 4) // Hardware HNP Only
+#define HNP_ABSEVAILD (1 << 3)
+#define HNP_ABUSVALID (1 << 2)
+#define HNP_MASSLVCHG (1 << 1) // Hardware HNP Only
+#define HNP_IDCHANGE (1)
+
+#define OTG_CORE_HINT_STEN (OTG_CORE_BASE+0x30) // 32bit core HNP int enable reg
+
+#define HNP_I2COTGINT_EN (1 << 15)
+#define HNP_AWAITBTO_EN (1 << 8)
+#define HNP_AIDLEBDTO_EN (1 << 7)
+#define HNP_SRPSUCFAIL_EN (1 << 6)
+#define HNP_SRPINT_EN (1 << 5)
+#define HNP_VBUSERROR_EN (1 << 4)
+#define HNP_ABSEVAILD_EN (1 << 3)
+#define HNP_ABUSVALID_EN (1 << 2)
+#define HNP_MASSLVCHG_EN (1 << 1)
+#define HNP_IDCHANGE_EN (1)
+
+
+#define OTG_CORE_CPUEPSEL_STAT (OTG_CORE_BASE+0x34)
+#define OTG_CORE_INTERRUPT_STEN (OTG_CORE_BASE+0x3c)
+
+/*! @} */
+
+
+/*!
+ * @name C.f. 23.11.11 Host Registers
+ */
+ /*! @{ */
+
+
+#define OTG_HOST_CONTROL (OTG_HOST_BASE+0x00) // 32bit host controller config reg
+
+#define HOST_CONTROL_HCRESET (1 << 31)
+#define HOST_CONTROL_RMTWUEN (1 << 4)
+
+#define HOST_CONTROL_HCUSBSTE_RESET (0 << 2)
+#define HOST_CONTROL_HCUSBSTE_RESUME (1 << 2)
+#define HOST_CONTROL_HCUSBSTE_OPERATIONAL (2 << 2)
+#define HOST_CONTROL_HCUSBSTE_SUSPEND (3 << 2)
+
+#define HOST_CONTROL_CTLBLKSR_11 (0)
+#define HOST_CONTROL_CTLBLKSR_21 (1)
+#define HOST_CONTROL_CTLBLKSR_41 (2)
+#define HOST_CONTROL_CTLBLKSR_81 (3)
+
+
+#define OTG_HOST_SINT_STAT (OTG_HOST_BASE+0x08) // 32bit host system int status reg
+
+#define HOST_PSCINT (1 << 6) // Port Status Change
+#define HOST_FMOFINT (1 << 5) // Frame Number Overflow
+#define HOST_HERRINT (1 << 4) // Host Error
+#define HOST_RESDETINT (1 << 3) // Resume Detected
+#define HOST_SOFINT (1 << 2) // Start of Frame
+#define HOST_DONEINT (1 << 1) // Done Register
+#define HOST_SORINT (1) // Schedule Overrun
+
+#define OTG_HOST_SINT_STEN (OTG_HOST_BASE+0x0C) // 32bit host system int enable reg
+
+#define HOST_PSCINT_EN (1 << 6) // Port Status Change
+#define HOST_FMOFINT_EN (1 << 5) // Frame Number Overflow
+#define HOST_HERRINT_EN (1 << 4) // Host Error
+#define HOST_RESDETINT_EN (1 << 3) // Resume Detected
+#define HOST_SOFINT_EN (1 << 2) // Start of Frame
+#define HOST_DONEINT_EN (1 << 1) // Done Register
+#define HOST_SORINT_EN (1) // Schedule Overrun
+
+#define OTG_HOST_XINT_STAT (OTG_HOST_BASE+0x18) // 32bit host X buf int status reg
+#define OTG_HOST_YINT_STAT (OTG_HOST_BASE+0x1C) // 32bit host Y buf int status reg
+
+#define OTG_HOST_XYINT_STEN (OTG_HOST_BASE+0x20) // 32bit host XY buf int enable reg
+#define OTG_HOST_XFILL_STAT (OTG_HOST_BASE+0x28) // 32bit host X filled status reg
+#define OTG_HOST_YFILL_STAT (OTG_HOST_BASE+0x2C) // 32bit host Y filled status reg
+
+#define OTG_HOST_ETD_EN (OTG_HOST_BASE+0x40) // 32bit host ETD enables reg
+#define OTG_HOST_DIR_ROUTE (OTG_HOST_BASE+0x48) // 32bit host direct routing reg
+#define OTG_HOST_IINT (OTG_HOST_BASE+0x4C) // 32bit host immediate interrupt reg
+
+#define OTG_HOST_EP_DSTAT (OTG_HOST_BASE+0x50) // 32bit host endpoints done status
+#define OTG_HOST_ETD_DONE (OTG_HOST_BASE+0x54) // 32bit host ETD done reg
+
+#define OTG_HOST_FRM_NUM (OTG_HOST_BASE+0x60) // 32bit host frame number reg
+#define OTG_HOST_LSP_THRESH (OTG_HOST_BASE+0x64) // 32bit host low speed threshold reg
+
+#define OTG_HOST_ROOTHUB_DESCA (OTG_HOST_BASE+0x68) // 32bit host root hub descriptor A
+#define OTG_HOST_ROOTHUB_DESCB (OTG_HOST_BASE+0x6C) // 32bit host root hub descriptor B
+/* @} */
+
+/*!
+ * @name C.f. 23.11.29
+ */
+/*! @{ */
+#define OTG_HOST_ROOTHUB_STATUS (OTG_HOST_BASE+0x70) // 32bit host root hub status reg
+
+#define ROOTHUB_STATUS_CLRMTWUE (1 << 31) // Clear Remote Wakeup Enable
+#define ROOTHUB_STATUS_OVRCURCHG (1 << 17) // Over Current Indicator Change
+#define ROOTHUB_STATUS_LOCPWRSC (1 << 16) // Local Power Status Change
+#define ROOTHUB_STATUS_DEVCONWUE (1 << 15) // Device Connect Wakeup Enable
+#define ROOTHUB_STATUS_OVRCURI (1 << 1) // Over Current Indicator
+#define ROOTHUB_STATUS_LOCPWRS (1) // Local Power Status
+/*! @} */
+
+
+/*!
+ * @name C.f. 23.11.30
+ */
+/*! @{ */
+#define OTG_HOST_PORT_STATUS_1 (OTG_HOST_BASE+0x74) // 32bit host port 1 status bits
+#define OTG_HOST_PORT_STATUS_2 (OTG_HOST_BASE+0x78) // 32bit host port 2 status bits
+#define OTG_HOST_PORT_STATUS_3 (OTG_HOST_BASE+0x7c) // 32bit host port 3 status bits
+
+#define PORT_STATUS_PRTRSTSC (1 << 20) // Port Reset Status Change
+#define PORT_STATUS_OVRCURIC (1 << 19) // Port Over Current Indicator Change
+#define PORT_STATUS_PRTSTATSC (1 << 18) // Port Suspend Status Change
+#define PORT_STATUS_PRTENBLSC (1 << 17) // Port Enable Status Change
+#define PORT_STATUS_CONNECTSC (1 << 16) // Connect Status Change
+
+#define PORT_STATUS_LSDEVCON (1 << 9) // Low Speed Device Attached
+#define PORT_STATUS_PRTPWRST (1 << 8) // Port Power Status
+
+#define PORT_STATUS_PRTRSTST (1 << 4) // Port Reset Status
+#define PORT_STATUS_PRTOVRCURI (1 << 3) // Port Over Current Indicator
+#define PORT_STATUS_PRTSUSPST (1 << 2) // Port Suspend Status
+#define PORT_STATUS_PRTENABST (1 << 1) // Port Enable Status
+#define PORT_STATUS_CURCONST (1) // Current Connect Status
+
+
+/* flags are the same for the interrupt, control and bulk transfer descriptors */
+// R1: sec 23.11.10.2,3 pg 23-44,47
+#define ETD_GET_COMPCODE(flags) ((flags >> 12) & 0xf)
+// R1: sec 23.11.10 Table 23-23 pg 23-40
+#define ETD_CC_NOERROR 0x0
+#define ETD_CC_CRCERR 0x1
+#define ETD_CC_BITSTUFFERR 0x2
+#define ETD_CC_DATATOGERR 0x3
+#define ETD_CC_STALL 0x4
+#define ETD_CC_DEVNOTRESPONDING 0x5
+#define ETD_CC_PIDFAILURE 0x6
+// Reserved 0x7
+#define ETD_CC_DATAOVERRUN 0x8
+#define ETD_CC_DATAUNDERRUN 0x9
+#define ETD_CC_ACK 0xA
+#define ETD_CC_NAK 0xB
+#define ETD_CC_BUFFEROVERRUN 0xC
+#define ETD_CC_BUFFERUNDERRUN 0xD
+#define ETD_CC_SCHEDOVERRUN 0xE
+#define ETD_CC_NOTACCESSED 0xF
+
+// R1: sec 23.11.10.2,3 pg 23-44,47 (contd)
+#define ETD_GET_ERRORCNT(flags) (((flags) >> 8) & 0xf)
+#define ETD_GET_DATATOGL(flags) (((flags) >> 6) & 0x3)
+#define ETD_SET_DATATOGL(v) (((v) & 0x3) << 6)
+#define ETD_GET_DELAYINT(flags) (((flags) >> 3) & 0x7)
+#define ETD_SET_DELAYINT(v) (((v) & 0x7) << 3)
+#define ETD_GET_BUFROUND(flags) (((flags) >> 2) & 0x1)
+#define ETD_SET_BUFROUND(v) (((v) & 0x1) << 2)
+#define ETD_GET_DIRPID(flags) ((flags) & 0x3)
+#define ETD_SET_DIRPID(v) ((v) & 0x3)
+#define ETD_FLIP_DIRPID(flags) ((flags) ^ 0x3) /* xor with 0x3 flips 2<->1 (IN<->OUT) */
+#define ETD_DIRPID_SETUP 0x0 // OUT
+#define ETD_DIRPID_OUT 0x1
+#define ETD_DIRPID_IN 0x2
+#define ETD_DIRPID_RESERVED 0x3
+
+// For non-isoc td.w3.val -
+#define ETD_GET_BUFSIZE(w3) (((w3) >> 21) & 0xfff)
+#define ETD_SET_BUFSIZE(v) (((v) & 0xfff) << 21)
+#define ETD_GET_TOTBYECNT(w3) ((w3) & 0xfffff)
+#define ETD_SET_TOTBYECNT(v) ((v) & 0xfffff)
+
+/* isoc flags shares the COMPCODE and DELAYINT bitfields with the other TDs,
+ but the remaining bits are different. */
+#define ETD_GET_AUTOISO(flags) (((flags) >> 11) & 0x1)
+#define ETD_SET_AUTOISO(v) (((v) & 0x1) << 11)
+#define ETD_GET_FRAMECNT(flags) (((flags) >> 8) & 0x1)
+#define ETD_SET_FRAMECNT(v) (((v) & 0x1) << 8)
+// For isoc td.w3.val -
+/* pkt1 and pkt0 use the COMPCODE bitfield, and add a PKTLEN field. */
+#define ETD_GET_PKTLEN(pkt) ((pkt) & 0x3ff)
+#define ETD_SET_PKTLEN(v) ((v) & 0x3ff)
+
+
+// ETD endpoint descriptor (etd->epd) bitfield access
+// R1: sec 23.11.10.1 pg 23-41 (word0)
+#define ETD_GET_SNDNAK(epd) (((epd) >> 30) & 0x1)
+#define ETD_SET_SNDNAK(v) (((v) & 0x1) << 30)
+#define ETD_GET_TOGCRY(epd) (((epd) >> 28) & 0x1)
+#define ETD_SET_TOGCRY(v) (((v) & 0x1) << 28)
+#define ETD_GET_HALTED(epd) (((epd) >> 27) & 0x1)
+#define ETD_SET_HALTED(v) (((v) & 0x1) << 27)
+#define ETD_GET_MAXPKTSIZ(epd) (((epd) >> 16) & 0x3ff)
+#define ETD_SET_MAXPKTSIZ(v) (((v) & 0x3ff) << 16)
+#define ETD_GET_FORMAT(epd) (((epd) >> 14) & 0x3)
+#define ETD_SET_FORMAT(v) (((v) & 0x3) << 14)
+#define ETD_FORMAT_CONTROL 0x0
+#define ETD_FORMAT_ISOC 0x1
+#define ETD_FORMAT_BULK 0x2
+#define ETD_FORMAT_INTERRUPT 0x3
+#define ETD_GET_SPEED(epd) (((epd) >> 13) & 0x1)
+#define ETD_SET_SPEED(v) (((v) & 0x1) << 13)
+#define ETD_SPEED_FULL 0x0
+#define ETD_SPEED_LOW 0x1
+#define ETD_GET_DIRECT(epd) (((epd) >> 11) & 0x3)
+#define ETD_SET_DIRECT(v) (((v) & 0x3) << 11)
+/* xor with 3 flips 2<->1 */
+#define ETD_FLIP_DIRECT(epd) ((epd) ^ (0x3 << 11))
+#define ETD_DIRECT_TD00 0x0
+#define ETD_DIRECT_OUT 0x1
+#define ETD_DIRECT_IN 0x2
+#define ETD_DIRECT_TD11 0x3
+#define ETD_GET_ENDPNT(epd) (((epd) >> 7) & 0xf)
+#define ETD_SET_ENDPNT(v) (((v) & 0xf) << 7)
+#define ETD_GET_ADDRESS(epd) ((epd) & 0x7f)
+#define ETD_SET_ADDRESS(v) ((v) & 0x7f)
+
+#if 0
+// etd_urb_state[] values
+#define ETD_URB_COMPLETED 0
+#define ETD_URB_SETUP_STATUS 1
+#define ETD_URB_SETUP_DATA 2
+#define ETD_URB_SETUP_START 3
+#define ETD_URB_BULK_START 4
+#define ETD_URB_BULKWZLP 5
+#define ETD_URB_BULKWZLP_START 6
+#define ETD_URB_INTERRUPT_START 7
+#define ETD_URB_ISOC_START 8
+#endif
+/*! @} */
+
+
+/*!
+ * @name C.f. 23.12.8 Function Registers
+ */
+/*! @{ */
+
+#define OTG_FUNC_CMD_STAT (OTG_FUNC_BASE+0x00) // 32bit func command status reg
+
+#define COMMAND_SOFTRESET (1 << 7)
+#define COMMAND_BADISOAP (1 << 3)
+#define COMMAND_SUPDET (1 << 2)
+#define COMMAND_RSMINPROG (1 << 1)
+#define COMMAND_RESETDET (1)
+
+
+#define OTG_FUNC_DEV_ADDR (OTG_FUNC_BASE+0x04) // 32bit func device address reg
+
+#define OTG_FUNC_SINT_STAT (OTG_FUNC_BASE+0x08) // 32bit func system int status reg
+
+#define SYSTEM_DONEREGINTDS (1 << 5)
+#define SYSTEM_SOFDETINT (1 << 4)
+#define SYSTEM_DONEREGINT (1 << 3)
+#define SYSTEM_SUSPDETINT (1 << 2)
+#define SYSTEM_RSMFININT (1 << 1)
+#define SYSTEM_RESETINT (1)
+
+#define OTG_FUNC_SINT_STEN (OTG_FUNC_BASE+0x0C) // 32bit func system int enable reg
+
+#define OTG_FUNC_SINT_STEN_CLR (OTG_FUNC_BASE+0x10C) // 32bit func system int enable clear reg
+
+#define SYSTEM_DONEREGINTDS_EN (1 << 5)
+#define SYSTEM_SOFDETINT_EN (1 << 4)
+#define SYSTEM_DONEREGINT_EN (1 << 3)
+#define SYSTEM_SUSPDETINT_EN (1 << 2)
+#define SYSTEM_RSMFININT_EN (1 << 1)
+#define SYSTEM_RESETINT_EN (1)
+
+
+#define OTG_FUNC_XINT_STAT (OTG_FUNC_BASE+0x10) // 32bit func X buf int status reg
+#define OTG_FUNC_YINT_STAT (OTG_FUNC_BASE+0x14) // 32bit func Y buf int status reg
+
+#define OTG_FUNC_XYINT_STEN (OTG_FUNC_BASE+0x18) // 32bit func XY buf int enable reg
+#define OTG_FUNC_XYINT_STEN_CLR (OTG_FUNC_BASE+0x118) // 32bit func XY buf int enable clear reg
+
+#define OTG_FUNC_XFILL_STAT (OTG_FUNC_BASE+0x1C) // 32bit func X filled status reg
+#define OTG_FUNC_YFILL_STAT (OTG_FUNC_BASE+0x20) // 32bit func Y filled status reg
+
+#define OTG_FUNC_EP_EN (OTG_FUNC_BASE+0x24) // 32bit func endpoints enable reg
+#define OTG_FUNC_EP_EN_CLR (OTG_FUNC_BASE+0x124) // 32bit func endpoints enable clear reg
+
+#define OTG_FUNC_EP_RDY (OTG_FUNC_BASE+0x28) // 32bit func endpoints ready reg
+#define OTG_FUNC_EP_RDY_CLR (OTG_FUNC_BASE+0x3C) // 32bit func endpoints ready clear reg
+
+#define OTG_FUNC_IINT (OTG_FUNC_BASE+0x2C) // 32bit func immediate interrupt reg
+#define OTG_FUNC_IINT_CLR (OTG_FUNC_BASE+0x12C) // 32bit func immediate interrupt clear reg
+
+#define OTG_FUNC_EP_DSTAT (OTG_FUNC_BASE+0x30) // 32bit func endpoints done status
+
+#define OTG_FUNC_EP_DEN (OTG_FUNC_BASE+0x34) // 32bit func endpoints done enable
+#define OTG_FUNC_EP_DEN_CLR (OTG_FUNC_BASE+0x134) // 32bit func endpoints done clear enable
+
+#define OTG_FUNC_EP_TOGGLE (OTG_FUNC_BASE+0x38) // 32bit func endpoints toggle bits
+#define OTG_FUNC_FRM_NUM (OTG_FUNC_BASE+0x3C) // 32bit func frame number reg
+
+
+
+
+
+
+#define EP0_STALL (1 << 31)
+#define EP0_SETUP (1 << 30)
+#define EP0_OVERRUN (1 << 29)
+#define EP0_AUTOISO (1 << 27)
+
+#define EP_FORMAT_CONTROL 0x0
+#define EP_FORMAT_ISOC 0x1
+#define EP_FORMAT_BULK 0x2
+#define EP_FORMAT_INTERRUPT 0x3
+
+/* generic endpoint register manipulations
+ */
+#define EP_OUT 0x1
+#define EP_IN 0x2
+#define EP_BOTH 0x3
+
+#define ETD_MASK(n) (1 << n)
+#define ep_num_both(n) (EP_BOTH << n)
+#define ep_num_dir(n, dir) ((dir ? EP_IN : EP_OUT) << (n*2))
+#define ep_num_out(n) ep_num_dir(n, USB_DIR_OUT)
+#define ep_num_in(n) ep_num_dir(n, USB_DIR_IN)
+
+
+/*! @} */
+
+/*!
+ * @name C.f. 23.13.3 DMA Registers
+ */
+/*! @{ */
+#define OTG_DMA_REV_NUM (OTG_DMA_BASE+0x000) // 32bit dma revision number reg
+#define OTG_DMA_DINT_STAT (OTG_DMA_BASE+0x004) // 32bit dma int status reg
+#define OTG_DMA_DINT_STEN (OTG_DMA_BASE+0x008) // 32bit dma int enable reg
+#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg
+#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg
+#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg
+#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg
+#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req
+#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req
+#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req
+#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req
+#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg
+#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg
+
+#define OTG_DMA_MISC_CTRL (OTG_DMA_BASE+0x040) // 32bit dma EP misc control reg
+#define OTG_DMA_MISC_ARBMODE (1 << 1)
+
+
+#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma ETD clear channel reg
+#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x04c) // 32bit dma EP clear channel reg
+
+#if 0
+#define OTG_DMA_ETD_CH_CLR (OTG_DMA_BASE+0x044) // 32bit dma ETD clear channel reg
+#define OTG_DMA_ETD_ERR (OTG_DMA_BASE+0x00C) // 32bit dma ETD error status reg
+#define OTG_DMA_ETD_EN (OTG_DMA_BASE+0x020) // 32bit dma ETD DMA enable reg
+#define OTG_DMA_ETD_ENXREQ (OTG_DMA_BASE+0x028) // 32bit dma ETD DMA enable Xtrig req
+#define OTG_DMA_ETD_ENXYREQ (OTG_DMA_BASE+0x030) // 32bit dma ETD DMA enble XYtrig req
+#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enble burst4 reg
+
+
+
+#define OTG_DMA_EP_CH_CLR (OTG_DMA_BASE+0x048) // 32bit dma EP clear channel reg
+#define OTG_DMA_EP_ERR (OTG_DMA_BASE+0x010) // 32bit dma EP error status reg
+#define OTG_DMA_EP_EN (OTG_DMA_BASE+0x024) // 32bit dma EP DMA enable reg
+#define OTG_DMA_EP_ENXREQ (OTG_DMA_BASE+0x02C) // 32bit dma EP DMA enable Ytrig req
+#define OTG_DMA_EP_ENXYREQ (OTG_DMA_BASE+0x034) // 32bit dma EP DMA enable XYtrig req
+#define OTG_DMA_ETD_BURST4 (OTG_DMA_BASE+0x038) // 32bit dma ETD DMA enable burst4 reg
+#define OTG_DMA_EP_BURST4 (OTG_DMA_BASE+0x03C) // 32bit dma EP DMA enable burst4 reg
+
+#endif
+
+
+#define dma_num_dir(n, dir) (n * 2 + (dir ? 1 : 0))
+#define dma_num_out(n) dma_num_dir(n, USB_DIR_OUT)
+#define dma_num_in(n) dma_num_dir(n, USB_DIR_IN)
+
+#define OTG_DMA_ETD_MSA(x) (OTG_DMA_BASE+0x100+x*4)
+#define OTG_DMA_EPN_MSA(x) (OTG_DMA_BASE+0x180+x*4)
+#define OTG_DMA_ETDN_BPTR(x) (OTG_DMA_BASE+0x280+x*4)
+#define OTG_DMA_EPN_BPTR(x) (OTG_DMA_BASE+0x284+x*4)
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * Warning: the MX21 memory mapped region appears to require 32-bit access only.
+ * As of this writing, only the ETD region has been tested, but it does show the
+ * following behavior:
+ *
+ * Step 1: *(volatile u32 *)(void*)0xe40243f8 == 00000000
+ * Step 2: *(volatile u16 *)(void*)0xe40243f8 = 0x0084;
+ * Step 3: *(volatile u32 *)(void*)0xe40243f8 == 00840084
+ * Step 4: *(volatile u8 *)(void*)(0xe40243f8+3) = 0x01;
+ * Step 5: *(volatile u32 *)(void*)0xe40243f8 == 01010101
+ *
+ * Any access to less than 32 bits is replicated as many times as required
+ * to make 32 bits, and the surrounding 32-bit region is changed.
+ *
+ * Just to add insult to injury, gcc 3.2.3 wants to store constants
+ * that are small enough using byte instructions. E.g.
+ *
+ * mm->host.System_Interrupt_Enables = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN |
+ * SIEN_DONEIEN | SIEN_SORIEN);
+ *
+ * becomes:
+ *
+ * ldrb r2, [r3, #140]
+ * mov r2, #123
+ * strb r2, [r3, #140]
+ * ldrb r2, [r3, #141]
+ * strb r1, [r3, #141]
+ * ldrb r2, [r3, #142]
+ * strb r1, [r3, #142]
+ * ldrb r2, [r3, #143]
+ * strb r1, [r3, #143]
+ *
+ * which, given the 32-bit access restrictions above,
+ * is not going to work very well. Changing the original assignment to:
+ *
+ * *&(mm->host.System_Interrupt_Enables) = (SIEN_PSCIEN | SIEN_FMOFIEN | SIEN_HERRIEN | SIEN_RESDETIEN |
+ * SIEN_DONEIEN | SIEN_SORIEN);
+ *
+ * which should be identical, from a language definition point of view,
+ * produces:
+ *
+ * mov r2, #123
+ * str r2, [r3, #140]
+ *
+ */
+
+
+/*!
+ * @name TransferDescriptors
+ *
+ * Note that the MX21 needs to run in little-endian mode for OTG, but
+ * since the memory can only be accessed in 32-bit quantities, anything
+ * smaller will be built in regular memory, then transfered one word at
+ * a time to the memory-mapped registers, and that word access will flip
+ * the order of bytes. u16 quantities survive this flipping because they
+ * get "pre-flipped" in regular memory.
+ *
+ *
+ * An Endpoint Transfer Descriptor (ETD) consists of 4x32bit words.
+ * The first word is the Endpoint Descriptor, the last three are
+ * the transfer descriptor. Transfer descriptors come in 3 formats,
+ * CONTROL/BULK, INTERRUPT, and ISOCRONOUS. Becasue of the memory
+ * access restrictions to less than 32 bits described above, only
+ * the words are mapped directly, The smaller fields are assembled
+ * into 32-bit words before being placed into the live ETD.
+ *
+ */
+ /*! @{ */
+
+/*!
+ * create a type for transfer_descriptor_w1
+ * @brief typedef struct transfer_descriptor_w1 transfer_descriptor_w1
+ *All three types of TD have the same format for w1.
+ */
+typedef struct transfer_descriptor_w1 {
+#if defined(LITTLE_ENDIAN)
+ u16 x;
+ u16 y;
+#else
+ u16 y;
+ u16 x;
+#endif
+} __attribute__ ((packed)) volatile transfer_descriptor_w1;
+
+/*!
+ * create a type for control_bulk_transfer_descriptor_w2
+ * @brief typedef struct control_bulk_transfer_descriptor_w2 control_bulk_transfer_descriptor_w2
+ * There are 3 different formats for w2.
+ * C.f. R1: sec 23.11.10.2 pg 23-43
+ */
+typedef struct control_bulk_transfer_descriptor_w2 {
+#if defined(LITTLE_ENDIAN)
+ u8 rtrydelay;
+ u8 reserved;
+ u16 flags;
+#else
+ u16 flags;
+ u8 reserved;
+ u8 rtrydelay;
+#endif
+} __attribute__ ((packed)) volatile control_bulk_transfer_descriptor_w2;
+
+/*!
+ *create an alias for interrupt_transfer_descriptor_w2
+ * @brief typedef struct interrupt_transfer_descriptor_w2 interrupt_transfer_descriptor_w2
+ * C.f. R1: sec 23.11.10.3 pg 23-46
+ * C.f. R1: sec 23.11.10.3 pg 23-46
+ */
+typedef struct interrupt_transfer_descriptor_w2 {
+#if defined(LITTLE_ENDIAN)
+ u8 polinterv;
+ u8 relpolpos;
+ u16 flags;
+#else
+ u16 flags;
+ u8 relpolpos;
+ u8 polinterv;
+#endif
+} __attribute__ ((packed)) volatile interrupt_transfer_descriptor_w2;
+
+/*!
+ * create a type for isoc_transfer_descriptor_w2
+ * @brief typedef struct isoc_transfer_descriptor_w2 isoc_transfer_descriptor * C.f. R1: sec 23.11.10.4 pg 23-48
+ */
+typedef struct isoc_transfer_descriptor_w2 {
+#if defined(LITTLE_ENDIAN)
+ u16 startfrm;
+ u16 flags;
+#else
+ u16 flags;
+ u16 startfrm;
+#endif
+} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w2;
+
+
+
+/*!
+ * create a type for isoc_transfer_descriptor_w3
+ * @brief typedef struct isoc_transfer_descriptor_w3 isoc_transfer_descriptor
+ *
+ * There are 2 different formats for w3.
+ * All but isoc use a packet size and 20-bit bytecount, accessed through w3.val.
+ */
+typedef struct isoc_transfer_descriptor_w3 {
+#if defined(LITTLE_ENDIAN)
+ u16 pkt0;
+ u16 pkt1;
+#else
+ u16 pkt1;
+ u16 pkt0;
+#endif
+} __attribute__ ((packed)) volatile isoc_transfer_descriptor_w3;
+
+/*!
+ * @name transfer_descriptor
+ */
+/*! @{ */
+/*! create an alias for transfer_descriptor
+ * @brief typedef struct transfer_descriptor transfer_descriptor
+ */
+typedef struct transfer_descriptor {
+ union {
+ u32 val;
+ transfer_descriptor_w1 bufsrtad;
+ } volatile w1;
+ union {
+ u32 val;
+ control_bulk_transfer_descriptor_w2 cb;
+ interrupt_transfer_descriptor_w2 intr;
+ isoc_transfer_descriptor_w2 isoc;
+ } volatile w2;
+ union {
+ u32 val;
+ isoc_transfer_descriptor_w3 isoc;
+ } volatile w3;
+} __attribute__ ((packed)) volatile transfer_descriptor;
+
+/*! @} */
+
+/*!
+ * @name endpoint_transfer_descriptor
+ * C.f. R1: sec 23.11.10 pg 23-41
+ */
+ /*! @{ */
+/*! create a type for endpoint_transfer_descriptor
+ * @brief typedef struct endpoint_transfer_descriptor endpoint_transfer_descriptor
+ */
+typedef struct endpoint_transfer_descriptor {
+ u32 epd;
+ transfer_descriptor td;
+} __attribute__ ((packed)) volatile endpoint_transfer_descriptor; // ETD
+
+/*! @} */
+/*!
+ * fs_host_port_stat() - get host start port address for ports 1-3
+ * @param n
+ * @returns port address
+ */
+static u32 inline fs_host_port_stat(int n)
+{
+ switch (n) {
+ default:
+ case 1:
+ return OTG_HOST_PORT_STATUS_1;
+ case 2:
+ return OTG_HOST_PORT_STATUS_2;
+ case 3:
+ return OTG_HOST_PORT_STATUS_3;
+ }
+}
+
+/*! @name clock switch functions */
+/* ********************************************************************************************** */
+
+void fs_func_clock_on(void);
+void fs_host_clock_on(void);
+void fs_func_clock_off(void);
+void fs_host_clock_off(void);
+void fs_main_clock_on(void);
+void fs_main_clock_off(void);
+void fs_set_transceiver_mode(int mode);
+
+
+
+
+
+/*! @} */
diff --git a/drivers/otg/hardware/mxc-hcd.c b/drivers/otg/hardware/mxc-hcd.c
new file mode 100644
index 000000000000..9f7647de5d5f
--- /dev/null
+++ b/drivers/otg/hardware/mxc-hcd.c
@@ -0,0 +1,1481 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-hcd.c - Freescale USBOTG aware Host Controller Driver (HCD)
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/platform/mxc/mxc-hcd.c|20070918011422|08412
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ * Tony Tang <tt@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc-hcd.c
+ * @brief Freescale USB Host Controller Driver
+ *
+ * This is a complete and self contained Linux 2.6 USB Host Driver.
+ *
+ * It also conforms to the requirements for use as an OTG HCD driver
+ * in the Belcarra OTG Stack.
+ *
+ * The hardware is an integrated design, so it also works in conjunction
+ * with the mxc OCD and PCD drivers to share the hardware under the direction
+ * of the OTG State Machine.
+ *
+ * The root hub is managed locally, specifically we clear all relevant status
+ * changes etc. The MXC USB interrupt is level triggered so this is required
+ * to stop interrupts.
+ *
+ * The actual OTG port status changes are propagated to the upper layers
+ * through a virtual root hub which will allow the root hub support in the
+ * upper layers to operate properly.
+ *
+ * @ingroup FSOTG
+ * @ingroup HCD
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+
+#include <core/hcd.h>
+
+#include <otg/usbp-hub.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-utils.h>
+#include <otg/otg-tcd.h>
+
+#include <otg/otg-hcd.h>
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include <asm/memory.h>
+
+#include "mxc-hcd.h"
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+void mxc_hcd_giveback_req_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req, int status);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+// For debugging...
+static char *dirpid_name[4] = { "SETUP", "OUT", "IN", "WRONG-PID"};
+//
+char *etd_urb_state_name[] = {
+ "COMPLETED", "SETUP_STATUS", "SETUP_DATA", "SETUP_PKT",
+ "BULK_START", "BULK_WZLP", "BULK_WZLP_START", "INTERRUPT_START",
+ "ISOC", "WRONG-STATE",
+};
+
+static char *format_name[4] = { "CONTROL", "ISO", "BULK", "INT", };
+
+/*!
+ * @struct cc_name
+ * @brief Map CC values to names
+ */
+static char *cc_name[] = {
+ "no error", "CRC error", "bitstuff error", "data toggle error",
+ "stall", "device not responding", "PID failure" "reserved",
+ "data overrun", "data underrun", "ACK", "NAK", "buffer overrun",
+ "buffer underrun", "schedule overrun" "not accessed"
+};
+
+
+/*!
+ * comp_code_to_status() - map condition cod3 to errno value
+ * @param cc condition code
+ * @return errno value
+ */
+static int comp_code_to_status(int cc)
+{
+ if (cc) TRACE_MSG2(HCD,"NON-ZERO CC: %d %s", cc,cc_name[cc]);
+ switch (cc) {
+ case ETD_CC_NOERROR: return 0;
+ case ETD_CC_CRCERR: return -EILSEQ;
+ case ETD_CC_BITSTUFFERR: return -EPROTO;
+ case ETD_CC_DATATOGERR: return -EPROTO; // I guess, nothing else looks better.
+ case ETD_CC_STALL: return -EPIPE;
+ case ETD_CC_DEVNOTRESPONDING: return -ETIMEDOUT;
+ case ETD_CC_PIDFAILURE: return -EPROTO;
+ case ETD_CC_DATAOVERRUN: return -EOVERFLOW;
+ case ETD_CC_DATAUNDERRUN: return -EREMOTEIO;
+ case ETD_CC_ACK: return -EPROTO; // For lack of anything better
+ case ETD_CC_NAK: return -EPROTO; // For lack of anything better
+ case ETD_CC_BUFFEROVERRUN: return -ECOMM;
+ case ETD_CC_BUFFERUNDERRUN: return -ENOSR;
+ case ETD_CC_SCHEDOVERRUN: return -ENOSPC;
+ case ETD_CC_NOTACCESSED: return -EPROTO; // For lack of anything better
+ default: return -EINVAL;
+ }
+}
+/* ********************************************************************************************* */
+/*! ETD Management
+ */
+
+/*!
+ * rel_etd_irq() - release etd
+ * @param mxc_hcd
+ * @param etdn
+ * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the
+ * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be
+ * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at
+ * regular priority.
+ */
+void rel_etd_irq(struct mxc_hcd *mxc_hcd, int etdn)
+{
+ TRACE_MSG1(HCD,"*****************etdn:%d",etdn);
+ fs_wl(etd_word(etdn, 0), 0);
+ fs_wl(etd_word(etdn, 1), 0);
+ fs_wl(etd_word(etdn, 2), 0);
+ fs_wl(etd_word(etdn, 3), 0);
+ mxc_hcd->active[etdn] = NULL;
+ //mxc_hcd->active_count--;
+}
+
+/*!
+ * get_etd_irq() - get etd
+ * @param mxc_hcd
+ * @param mxc_req
+ * @return int
+ * {get,rel}_etd maintain a free list of ETDs by saving the index of the next free ETD in the
+ * etd->xbufsrtad field. The list is terminated by 0x0000ffff. List manipulation needs to be
+ * protected by irq_lock, since ETDs are released in the interrupt handler, but allocated at
+ * regular priority.
+ */
+static int get_etd_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req)
+{
+ int etdn;
+ for (etdn = 0; etdn < NUM_ETDS; etdn++)
+ UNLESS (mxc_hcd->active[etdn]) {
+ mxc_hcd->active[etdn] = mxc_req;
+ //mxc_hcd->active_count++;
+ return etdn;
+ }
+ TRACE_MSG0(HCD,"error");
+ return -ENOMEM;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! Data Buffer Management
+ */
+
+/*!
+ * @name DataBuffs
+ *
+ * There is no predetermined size for data buffers, so
+ * this structure was chosen as an arbitrary, but generally
+ * adequate size.
+ */
+/*! @{ */
+
+/*!
+ * get_data_buff() - get data buff
+ * Get the next available free data_buff, return NULL if none available.
+ */
+static __inline__ fs_data_buff *get_data_buff(struct mxc_hcd *mxc_hcd)
+{
+ unsigned long flags;
+ fs_data_buff *db = NULL;
+ u16 ndx;
+ local_irq_save(flags);
+ if (0xffff != (ndx = mxc_hcd->free_buffs)) {
+ mxc_hcd->free_buffs = mxc_hcd->buff_list[ndx];
+ db = ndx + (fs_data_buff *)OTG_DATA_BASE;
+ }
+ local_irq_restore(flags);
+ return db;
+}
+
+/*!
+ * rel_data_buff() - release data buff
+ * Release db to the pool of available data_buffs.
+ */
+fs_data_buff *rel_data_buff(struct mxc_hcd *mxc_hcd, fs_data_buff *db)
+{
+ unsigned long flags;
+ u16 ndx;
+ RETURN_NULL_UNLESS (db);
+ local_irq_save(flags);
+ ndx = db - (fs_data_buff *)OTG_DATA_BASE;
+ mxc_hcd->buff_list[ndx] = mxc_hcd->free_buffs;
+ mxc_hcd->free_buffs = ndx;
+ local_irq_restore(flags);
+ return NULL;
+}
+
+/*!
+ * data_buff_boff() - get data buffer offset given address
+ */
+static __inline__ u16 data_buff_boff(fs_data_buff *db)
+{
+ return((u16)(((void *) db) - ((void *) OTG_DATA_BASE)));
+}
+
+/*!
+ * data_buff_addr() - get data buffer address given offset
+ * Return the address of the fs_data_buffer that is boff bytes from the start of data buffer memory.
+ */
+static __inline__ fs_data_buff *data_buff_addr(u16 boff)
+{
+ return(boff + ((void *) OTG_DATA_BASE));
+}
+/*! @} */
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! Data Tranfer Handling
+ */
+
+u8 dmabuf[128];
+
+/*!
+ * mxc_hcd_start_etd_irq() - start an etd
+ * @param etdn
+ * @param len
+ * @param dma
+ * @param out
+ *
+ * This will start a request by enabling the specified etd.
+ *
+ * XXX I don't know why this doesn't work correctly unless DMA is enabled.
+ * But enabling DMA with NULL data address is not a greate idea... so
+ * we check and give a dummy buffer for it to use.
+ *
+ */
+static void mxc_hcd_start_etd_irq(int etdn, int len, u32 dma, int out)
+{
+ TRACE_MSG4(HCD, "etdn: %d len: %d dma: %x out: %d", etdn, len, dma, out);
+
+ fs_wl(OTG_DMA_ETD_MSA(etdn), dma ? dma : (int)virt_to_phys(dmabuf));
+
+ /* check for DMA alignment - downgrade if neccessary */
+ if (dma & 0xf) {
+ fs_andl(OTG_DMA_ETD_BURST4, ~ETD_MASK(etdn));
+ TRACE_MSG2(HCD, "RESET ETD BURST4[%2d]: %08x", ETD_MASK(etdn), fs_rl(OTG_DMA_ETD_BURST4));
+ }
+ else {
+ fs_orl(OTG_DMA_ETD_BURST4, ETD_MASK(etdn));
+ TRACE_MSG2(HCD, "SET ETD BURST4[%2d]: %08x", ETD_MASK(etdn), fs_rl(OTG_DMA_ETD_BURST4));
+ }
+
+
+ if (len || out) fs_wl(OTG_DMA_ETD_EN, ETD_MASK(etdn));
+
+ fs_orl(OTG_HOST_IINT, ETD_MASK(etdn)); // Don't wait until SOF, interrupt ASAP.
+ fs_orl(OTG_HOST_ETD_DONE, ETD_MASK(etdn)); // Interrupt on ETD done (forget DMA interrupt).
+ fs_wl(OTG_HOST_ETD_EN, ETD_MASK(etdn));
+}
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+
+/*! mxc_hcd_start_req_irq - start a request
+ * @param mxc_hcd
+ * @param mxc_req
+ *
+ * @return non-zero if no resources available
+ *
+ * This function will attempt to find the resources to perform
+ * a request and will start it if the required resources are
+ * available.
+ *
+ * A non-zero return value signals a failure to find resources.
+ *
+ * Requests that are not possible to start for other reasons are
+ * not signalled back to the caller, the urb is simply given
+ * back to the hcd driver.
+ *
+ * XXX it is possible that too many receive urbs could tie up all
+ * existing etds. It would be possible to dequeue a receive urb
+ * temporarily and then restart.
+ */
+static int mxc_hcd_start_req_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req)
+{
+ struct urb *urb = mxc_req->urb;
+ struct usb_device *udev = urb->dev;
+ /*struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; */
+
+ int address = usb_pipedevice(urb->pipe) ?
+ (usb_pipedevice(urb->pipe) % MXC_MAX_USB_ADDRESS + 1) : 0;
+ int is_out = usb_pipein(urb->pipe) ? 0 : 1;
+ int endpoint = usb_pipeendpoint(urb->pipe);
+ u32 dma = mxc_req->transfer_dma;
+ int len = urb->transfer_buffer_length;
+
+ u32 other;
+
+ int pid;
+ int toggle;
+
+ transfer_descriptor td;
+ int fmt;
+ int dir;
+
+ endpoint_transfer_descriptor *sdp = NULL;
+
+ TRACE_MSG2(HCD, "mxc_req: %x urb: %x", mxc_req, urb);
+#if 0
+ if (is_out) {
+ int i;
+ u8 *cp = urb->transfer_buffer;
+ TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length);
+ for (i = 0; i < urb->actual_length; i+= 8)
+ TRACE_MSG8(HCD, "SENT %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]);
+
+ }
+ if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+ u8 *cp = (u8 *) urb->setup_packet;
+ TRACE_MSG8(HCD, "SETUP %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ }
+#endif
+
+ /* verify that urb status is ok */
+
+ if (urb->status != -EINPROGRESS) {
+ TRACE_MSG1(HCD, "bad status for urb: %08x status: %d, urb rejected.", urb->status);
+ list_del(&mxc_req->queue);
+ mxc_hcd_giveback_req_irq(mxc_hcd, mxc_req, -EPIPE);
+ return 0;
+ }
+
+ /* verify that this device endpoint is inactive */
+
+ if (mxc_hcd->ep[address][EPNUM(endpoint, is_out)]) {
+ TRACE_MSG3(HCD, "endpoint active for urb: %08x status: %d endpoint: %d out: %d.", urb->status, endpoint, is_out);
+ return 0;
+ }
+
+ /* verify we have hardware resources */
+
+ RETURN_ENOMEM_IF(mxc_hcd->active_count > NUM_ETDS);
+
+ /* 1. Allocate resources:
+ * a. X,Y buffers
+ * b. ETD (& matching DMA)
+ * 2. Set the registers
+ */
+ mxc_req->etdn = get_etd_irq(mxc_hcd, mxc_req);
+ mxc_req->x = get_data_buff(mxc_hcd);
+ mxc_req->y = get_data_buff(mxc_hcd);
+ mxc_req->epnum = EPNUM(endpoint, is_out);
+
+ UNLESS (mxc_req->x && mxc_req->y && (mxc_req->etdn != -1)) {
+ printk(KERN_INFO"%s: NO RESOURCES\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "NO RESOURCES");
+ if (mxc_req->x) mxc_req->x = rel_data_buff(mxc_hcd, mxc_req->x);
+ if (mxc_req->y) mxc_req->y = rel_data_buff(mxc_hcd, mxc_req->y);
+ mxc_req->x = mxc_req->y = NULL;
+ mxc_req->etdn = -1;
+ return -ENOMEM; // no sense in trying anymore...
+ }
+
+ mxc_hcd->active_count++;
+
+ /* set hcd_dev->ep[] so we can find specific request from hcd_dev, indexed by EPNUM(endpoint,is_out)
+ * set mxc_hcd->active[] so we can find requests from hardware POV, indexed by etdn
+ */
+ mxc_hcd->ep[address][mxc_req->epnum] = mxc_req;
+ memset((void *)&td, 0, sizeof(td));
+ td.w1.bufsrtad.y = data_buff_boff(mxc_req->y);
+ td.w1.bufsrtad.x = data_buff_boff(mxc_req->x);
+
+ /* we have request, we have urb, we have resources */
+
+ TRACE_MSG5(HCD,"urb: %08lx ep: %d %s len: %d etdn: %d",
+ (u32)(void*)urb, endpoint, (is_out?"OUT":"IN"), urb->actual_length, mxc_req->etdn);
+
+#if 0
+ if (usb_endpoint_halted(urb->dev, endpoint, is_out)) {
+ TRACE_MSG4(HCD,"urb for halted endpoint urb: %08lx dev: %d ep: %d dir: %c",
+ urb, dev_addr, endpoint, (is_out?'O':'I'));
+ urb->actual_length = 0;
+ mxc_hcd_giveback_urb(urb,-EPIPE);
+ return -EINVAL;
+ }
+#endif
+
+ /* Check pid, setup pid and out flags */
+ switch ( (usb_pipetype(urb->pipe) == PIPE_CONTROL) ? USB_PID_SETUP : ((is_out ? USB_PID_OUT : USB_PID_IN))) {
+ default:
+ case USB_PID_SETUP:
+ pid = ETD_DIRPID_SETUP;
+ is_out = 1;
+ break;
+ case USB_PID_OUT:
+ pid = ETD_DIRPID_OUT;
+ break;
+ case USB_PID_IN:
+ pid = ETD_DIRPID_IN;
+ break;
+ }
+
+ TRACE_MSG7(HCD, "len: %d endpoint: %02x pid: %d format: %d dev_addr: %d %s %s",
+ len, endpoint, pid, usb_pipetype(urb->pipe), usb_pipedevice(urb->pipe),
+ dirpid_name[pid], format_name[usb_pipetype(urb->pipe)]);
+
+ switch (usb_pipetype(urb->pipe)) {
+ default:
+ case PIPE_CONTROL:
+ TRACE_MSG0(HCD, "PIPE_CONTROL");
+ // XXX send set feature otg enable IFF set configuration and
+ // to first device and previously otg descriptor was seen.
+ // overload toggle with data phase direction (setup is always toggle 0)
+ // toggle == TRUE --> OUT (HOST2DEVICE --> OUT)
+
+ toggle = ((((struct usb_ctrlrequest *) urb->setup_packet)->bRequestType & USB_ENDPOINT_DIR_MASK) ==
+ USB_DIR_OUT);
+
+ //other = (u32) (void *) urb->setup_packet;
+
+ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len));
+
+ fmt = ETD_FORMAT_CONTROL;
+ dir = ETD_DIRECT_TD00;
+
+ /* build setup data/status phase ETD for use after completion of 8 byte setup packet
+ */
+ dma = mxc_req->setup_dma;
+ sdp = &mxc_req->sdp_etd;
+ memset((void*)sdp, 0, sizeof(endpoint_transfer_descriptor));
+
+ /* toggle is constant 0 for setup packet, and 1 for data and status phases,
+ * so toggle was overloaded to handle data phase direction. If len == 0, there
+ * is no data phase, so direction is IN.
+ */
+ sdp->td.w1.val = td.w1.val;
+ sdp->td.w2.cb.flags = ETD_SET_DATATOGL((0x2|1)) | // toggle is always 1, for data or status phases.
+ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames)
+ ETD_SET_BUFROUND(1) | // Allow short recv xfers
+ ETD_SET_DIRPID(((toggle && len > 0)? ETD_DIRPID_OUT:ETD_DIRPID_IN));
+ sdp->td.w2.cb.reserved = 0;
+ sdp->td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure.
+ sdp->td.w3.val = ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len);
+
+ /* build SETUP ETD for initial 8 byte setup packet
+ */
+ len = 8;
+ td.w2.cb.flags = ETD_SET_DATATOGL((0x2|0)) | // starting toggle is here, not TOGCRY, and is always 0.
+ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames)
+ ETD_SET_BUFROUND(1) | // Allow short recv xfers
+ ETD_SET_DIRPID(pid);
+ td.w2.cb.reserved = 0;
+ td.w2.cb.rtrydelay = 1; // Number of frames to wait before retry on failure.
+ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len));
+
+ mxc_req->etd_urb_state = ETD_URB_SETUP_START;
+
+ //TRACE_MSG3(HCD,"SETUP pkt, len: %d : %08x : %08x",len,*((u32*)(void*)data),*(1+(u32*)(void*)data));
+
+ /* Complete the setup data phase ETD.
+ */
+ sdp->epd = ETD_SET_MAXPKTSIZ ( usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) |
+ ETD_SET_TOGCRY(0) |
+ ETD_SET_FORMAT(ETD_FORMAT_BULK) |
+ ETD_SET_SPEED(((((urb->pipe) >> 26) & 1)? ETD_SPEED_LOW : ETD_SPEED_FULL)) |
+ ETD_SET_DIRECT(ETD_DIRECT_TD00) |
+ ETD_SET_ENDPNT(endpoint) |
+ ETD_SET_ADDRESS(usb_pipedevice(urb->pipe));
+
+ break;
+
+ case PIPE_BULK:
+ TRACE_MSG0(HCD, "PIPE_BULK");
+ toggle = usb_gettoggle(urb->dev, endpoint, is_out);
+ other = (urb->transfer_flags & URB_ZERO_PACKET);
+ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len));
+ fmt = ETD_FORMAT_BULK;
+ dir = ETD_DIRECT_TD00;
+
+ TRACE_MSG1(HCD,"BULK, starting toggle %x",toggle);
+ td.w2.cb.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY
+ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames)
+ ETD_SET_BUFROUND(1) | // Allow short recv xfers
+ ETD_SET_DIRPID(pid);
+ td.w2.cb.reserved = 0;
+ td.w2.cb.rtrydelay = 0; // Number of frames to wait before retry on failure.
+
+ /* Set the state to indicate if a trailing ZLP is required.
+ */
+ mxc_req->etd_urb_state = (other && is_out && !(len % usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) ?
+ ETD_URB_BULKWZLP_START : ETD_URB_BULK_START;
+ break;
+
+ case PIPE_INTERRUPT:
+ TRACE_MSG0(HCD, "PIPE_INTERRUPT");
+ toggle = usb_gettoggle(urb->dev, endpoint, is_out);
+ other = urb->interval;
+ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len));
+ fmt = ETD_FORMAT_INTERRUPT;
+ dir = ETD_DIRECT_TD00;
+
+ TRACE_MSG1(HCD,"INTERRUPT, starting toggle %x",toggle);
+ td.w2.intr.flags = ETD_SET_DATATOGL((0x2|toggle)) | // starting toggle is here, not TOGCRY
+ ETD_SET_DELAYINT(0) | // when done, interrupt ASAP (wait for 0 frames)
+ ETD_SET_BUFROUND(1) | // Allow short recv xfers
+ ETD_SET_DIRPID(pid);
+
+ td.w2.intr.relpolpos = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xff; // Start next frame
+
+ td.w2.intr.polinterv = other & 0xff;
+ mxc_req->etd_urb_state = ETD_URB_INTERRUPT_START;
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ TRACE_MSG0(HCD, "PIPE_ISOCHRONOUS");
+ toggle = 0;
+ other = urb->interval;
+ td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(len));
+
+ fmt = ETD_FORMAT_ISOC;
+ dir = is_out ? ETD_DIRECT_OUT : ETD_DIRECT_IN;
+ td.w2.isoc.startfrm = (fs_rl(OTG_HOST_FRM_NUM) + 1) & 0xffff; // next frame
+ if (len > 1023) {
+ // Two frames needed, send 1023 in the first, remainder in the second.
+ td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(1);
+ td.w3.isoc.pkt0 = ETD_SET_PKTLEN(1023);
+ td.w3.isoc.pkt1 = ETD_SET_PKTLEN(len-1023);
+ }
+ else {
+ // Only one frame needed, send it all at once.
+ td.w2.isoc.flags = ETD_SET_DELAYINT(0) | ETD_SET_AUTOISO(0) | ETD_SET_FRAMECNT(0);
+ td.w3.isoc.pkt0 = ETD_SET_PKTLEN(len);
+ td.w3.isoc.pkt1 = 0;
+ }
+ mxc_req->etd_urb_state = ETD_URB_ISOC_START;
+ break;
+ }
+
+ /* copy etd into hardware registers */
+
+ fs_wl(etd_word(mxc_req->etdn, 0),
+ ETD_SET_MAXPKTSIZ ( usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) |
+ ETD_SET_TOGCRY(0) |
+ ETD_SET_FORMAT(fmt) |
+ ETD_SET_SPEED(((((urb->pipe) >> 26) & 1)? ETD_SPEED_LOW : ETD_SPEED_FULL)) |
+ ETD_SET_DIRECT(dir) |
+ ETD_SET_ENDPNT(endpoint) |
+ ETD_SET_ADDRESS(usb_pipedevice(urb->pipe)));
+
+ fs_wl(etd_word(mxc_req->etdn, 1), td.w1.val);
+ fs_wl(etd_word(mxc_req->etdn, 2), td.w2.val);
+ fs_wl(etd_word(mxc_req->etdn, 3), td.w3.val);
+
+ TRACE_MSG5(HCD, "ETD[%02d] %08x %08x %08x %08x", mxc_req->etdn,
+ fs_rl(etd_word(mxc_req->etdn, 0)), fs_rl(etd_word(mxc_req->etdn, 1)),
+ fs_rl(etd_word(mxc_req->etdn, 2)), fs_rl(etd_word(mxc_req->etdn, 3)));
+
+ /* housekeeping before actual start */
+
+ urb->actual_length = 0;
+ list_del(&mxc_req->queue);
+
+ mxc_hcd_start_etd_irq(mxc_req->etdn, len, dma, is_out);
+
+ return 0; // allow scheduler to continue
+}
+
+/*! mxc_hcd_schedule_irq -
+ * @param mxc_hcd
+ *
+ * This function scans inactive list for work and will start
+ * anything it can find assuming no conflicts and that there
+ * are resources available.
+ *
+ * This is called from the enqueue function and the hcd interrupt handler.
+ *
+ */
+void mxc_hcd_schedule_irq(struct mxc_hcd *mxc_hcd)
+{
+ struct mxc_req *mxc_req;
+ struct mxc_req *mxc_req_save;
+
+ /* iterate across list of active urbs, normally this will be a very
+ * short list.... stop if non-zero which means there are no more
+ * resources.
+ *
+ * N.B. use _safe version of macro because we are deleting entries.
+ */
+ list_for_each_entry_safe(mxc_req, mxc_req_save, &mxc_hcd->inactive, queue) {
+ BREAK_IF(mxc_hcd_start_req_irq(mxc_hcd, mxc_req));
+ }
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * mxc_hcd_finish_req_irq() - called to complete transfer
+ * @param mxc_hcd
+ * @param mxc_req
+ * @param urb
+ * @param killed
+ *
+ * This function will finish a request and give the urb back to the hcd driver.
+ *
+ * Control Transfers may be restarted to do the status phase done and/or
+ * send a ZLP.
+ *
+ * Bulk transfers may be restarted to send a ZLP.
+ *
+ */
+void mxc_hcd_finish_req_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req, struct urb *urb, int killed)
+{
+ int cc = 0;
+ int next_toggle = 0;
+ int format = 0;
+ u32 remaining = 0;
+ fs_data_buff *x;
+ fs_data_buff *y;
+ endpoint_transfer_descriptor res;
+
+ struct usb_device *dev = urb->dev;
+ struct usb_bus *bus = dev->bus;
+ struct usb_device *udev = urb->dev;
+// struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv;
+ int is_out = usb_pipeout(urb->pipe);
+ int address = usb_pipedevice(urb->pipe) ?
+ (usb_pipedevice(urb->pipe) % MXC_MAX_USB_ADDRESS + 1) : 0;
+ int endpoint = usb_pipeendpoint(urb->pipe);
+ TRACE_MSG6(HCD, "urb: %x mxc_req: %x etdn: %d state: %d %s %s",
+ urb, mxc_req, mxc_req->etdn, mxc_req->etd_urb_state,
+ etd_urb_state_name[mxc_req->etd_urb_state], (killed?"KILLED":""));
+
+ /* Disable/Abort DMA. R1: sec 23.13.16 pg 23-103
+ * (supposed to auto-clear on DMA completion, but this shouldn't hurt).
+ */
+ fs_wl(OTG_DMA_ETD_CH_CLR, ETD_MASK(mxc_req->etdn));
+
+ if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(mxc_req->etdn))
+ fs_andl(OTG_HOST_ETD_EN, ~ETD_MASK(mxc_req->etdn)); // This register IS write 0 to disable. Really.
+
+ if ( fs_rl(OTG_HOST_ETD_DONE) & ETD_MASK(mxc_req->etdn))
+ fs_andl(OTG_HOST_ETD_DONE, ~ETD_MASK(mxc_req->etdn)); // This register IS write 0 to disable. Really.
+
+ // Hardware should have disabled ETD, but it doesn't hurt to check.
+ if (fs_rl(OTG_HOST_ETD_EN) & ETD_MASK(mxc_req->etdn))
+ fs_wl(OTG_HOST_ETD_EN, ETD_MASK(mxc_req->etdn)); // This register IS write 1 to clear. Really.
+
+ if (fs_rl(OTG_HOST_XINT_STAT) & ETD_MASK(mxc_req->etdn))
+ fs_wl(OTG_HOST_XINT_STAT, ETD_MASK(mxc_req->etdn)); // This register IS write 1 to clear. Really.
+
+ if (fs_rl(OTG_HOST_YINT_STAT) & ETD_MASK(mxc_req->etdn))
+ fs_wl(OTG_HOST_YINT_STAT, ETD_MASK(mxc_req->etdn)); // This register should be write 1 to clear.
+
+
+ if (fs_rl(OTG_HOST_XYINT_STEN) & ETD_MASK(mxc_req->etdn))
+ fs_andl(OTG_HOST_XYINT_STEN, ~ETD_MASK(mxc_req->etdn)); // This register should be write 0 to disable.
+
+ if (fs_rl(OTG_HOST_XFILL_STAT) & ETD_MASK(mxc_req->etdn))
+ fs_wl(OTG_HOST_XFILL_STAT, ETD_MASK(mxc_req->etdn)); // This register IS write 1 to clear. Really.
+
+ if (fs_rl(OTG_HOST_YFILL_STAT) & ETD_MASK(mxc_req->etdn))
+ fs_wl(OTG_HOST_YFILL_STAT, ETD_MASK(mxc_req->etdn)); // This register should be write 1 to clear.
+
+ // Extract results
+ res.epd = fs_rl(etd_word(mxc_req->etdn, 0));
+ res.td.w1.val = fs_rl(etd_word(mxc_req->etdn, 1));
+ res.td.w2.val = fs_rl(etd_word(mxc_req->etdn, 2));
+ res.td.w3.val = fs_rl(etd_word(mxc_req->etdn, 3));
+
+ fs_wl(OTG_HOST_EP_DSTAT, ETD_MASK(mxc_req->etdn));
+
+ //TRACE_MSG1(HCD,"OTG_HOST_ETD_DONE: %08x", fs_rl(OTG_HOST_ETD_DONE));
+
+ //if ((ETD_URB_BULK_START == mxc_req->etd_urb_state || (ETD_URB_BULKWZLP_START == mxc_req->etd_urb_state)) &&
+ // (ETD_GET_DIRPID(res.td.w2.cb.flags) != ETD_DIRPID_IN)
+ // )
+ TRACE_MSG4(HCD,"RES: epd: %08x w1: %08x w2: %08x w3: %08x", res.epd,res.td.w1.val,res.td.w2.val,res.td.w3.val);
+
+ if (ETD_GET_HALTED(res.epd))
+ TRACE_MSG0(HCD,"endpoint HALTED");
+
+ format = ETD_GET_FORMAT(res.epd);
+
+ switch (mxc_req->etd_urb_state) {
+ case ETD_URB_SETUP_START:
+ /* Just finished the setup packet.
+ * Go to data or status phase of setup packet while we still have the ETD and data buffers allocated.
+ */
+ if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) {
+
+ endpoint_transfer_descriptor *sdp = &mxc_req->sdp_etd;
+ int len = ETD_GET_TOTBYECNT(sdp->td.w3.val);
+ void *data = mxc_req->urb->transfer_buffer;
+ u32 dma = mxc_req->transfer_dma;
+ mxc_req->etd_urb_state = ((len > 0) ? ETD_URB_SETUP_DATA : ETD_URB_SETUP_STATUS);
+
+ // Copy into the active ETD and start it.
+ fs_wl(etd_word(mxc_req->etdn, 0), sdp->epd);
+ fs_wl(etd_word(mxc_req->etdn, 1), sdp->td.w1.val);
+ fs_wl(etd_word(mxc_req->etdn, 2), sdp->td.w2.val);
+ fs_wl(etd_word(mxc_req->etdn, 3), sdp->td.w3.val);
+ // watch out for ETD_DIRPID_SETUP
+
+ mxc_hcd_start_etd_irq(mxc_req->etdn, len, dma,
+ (ETD_GET_DIRPID(sdp->td.w2.cb.flags) != ETD_DIRPID_IN));
+
+ TRACE_MSG1(HCD,"SETUP %s Phase started",((len > 0) ? "DATA" : "STATUS"));
+ return;
+ }
+
+ /* Something is wrong, finish the URB as is. */
+ remaining = ETD_GET_TOTBYECNT(fs_rl(etd_word(mxc_req->etdn, 3)));
+ format = PIPE_CONTROL;
+ break;
+
+ case ETD_URB_SETUP_DATA:
+ /* Go to status phase of setup packet while we still have the ETD and data buffers allocated.
+ * Save the data phase length for after status.
+ */
+ if (!killed && ETD_CC_NOERROR == (cc = ETD_GET_COMPCODE(res.td.w2.cb.flags))) {
+ endpoint_transfer_descriptor *sdp = &mxc_req->sdp_etd;
+ mxc_req->remaining = ETD_GET_TOTBYECNT(res.td.w3.val);
+ mxc_req->etd_urb_state = ETD_URB_SETUP_STATUS;
+
+ /* Rebuild data phase sdp for status phase with opposite direction, and 0 length.
+ */
+
+ sdp->td.w2.cb.flags = ETD_FLIP_DIRPID(sdp->td.w2.cb.flags);
+ sdp->td.w3.val = (ETD_SET_BUFSIZE(sizeof(fs_data_buff)-1) | ETD_SET_TOTBYECNT(0));
+
+ /* Copy into the active ETD and start it.
+ */
+ fs_wl(etd_word(mxc_req->etdn, 0), sdp->epd);
+ fs_wl(etd_word(mxc_req->etdn, 1), sdp->td.w1.val);
+ fs_wl(etd_word(mxc_req->etdn, 2), sdp->td.w2.val);
+ fs_wl(etd_word(mxc_req->etdn, 3), sdp->td.w3.val);
+ mxc_hcd_start_etd_irq(mxc_req->etdn, 0, 0,
+ (ETD_GET_DIRPID(sdp->td.w2.cb.flags) != ETD_DIRPID_IN));
+ TRACE_MSG0(HCD,"SETUP STATUS Phase started");
+ return;
+ }
+
+ /* Something is wrong, finish the URB as is. */
+ remaining = ETD_GET_TOTBYECNT(res.td.w3.val);
+ format = PIPE_CONTROL;
+ break;
+
+ case ETD_URB_SETUP_STATUS:
+ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags);
+ remaining = mxc_req->remaining;
+ mxc_req->remaining = 0;
+ format = PIPE_CONTROL;
+ break;
+
+ case ETD_URB_BULKWZLP_START:
+ /* Need a trailing ZLP.
+ */
+ next_toggle = ETD_GET_TOGCRY(res.epd);
+ if (!killed && ETD_CC_NOERROR == ETD_GET_COMPCODE(res.td.w2.cb.flags)) {
+ // Since TOTBYECNT should be 0, re-enabling the same ETD ought to give a ZLP.
+ mxc_req->etd_urb_state = ETD_URB_BULKWZLP;
+ // Save the data phase length for after ZLP.
+ // mxc_hcd->sdp_data[mxc_req->etdn] = (void *) ETD_GET_TOTBYECNT(res.td.w3.val);
+ // FIXME - verify toggle OK
+
+ mxc_hcd_start_etd_irq(mxc_req->etdn, 0, 0, TRUE/*always OUT*/);
+
+ TRACE_MSG0(HCD,"ZLP started");
+ return;
+ }
+ // Something is wrong, finish the URB as is.
+ remaining = ETD_GET_TOTBYECNT(res.td.w3.val);
+ format = PIPE_BULK;
+ break;
+
+ case ETD_URB_BULKWZLP:
+ /* Trailing ZLP now finished. */
+ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags);
+ next_toggle = ETD_GET_TOGCRY(res.epd);
+ /* remaining must be zero */
+ remaining = 0;
+ format = PIPE_BULK;
+ break;
+
+ case ETD_URB_BULK_START:
+ cc = ETD_GET_COMPCODE(res.td.w2.cb.flags);
+ next_toggle = ETD_GET_TOGCRY(res.epd);
+ remaining = ETD_GET_TOTBYECNT(res.td.w3.val);
+ format = PIPE_BULK;
+ break;
+
+ case ETD_URB_INTERRUPT_START:
+ cc = ETD_GET_COMPCODE(res.td.w2.intr.flags);
+ remaining = ETD_GET_TOTBYECNT(res.td.w3.val);
+ next_toggle = usb_gettoggle(urb->dev,usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe)) ? 0 : 1;
+ format = PIPE_INTERRUPT;
+ break;
+
+ case ETD_URB_ISOC_START:
+ remaining = ETD_GET_PKTLEN(res.td.w3.isoc.pkt0);
+ cc = ETD_GET_COMPCODE(res.td.w3.isoc.pkt0);
+ if (ETD_GET_FRAMECNT(res.td.w2.isoc.flags)) {
+ // There were two packets in this transfer
+ remaining += ETD_GET_PKTLEN(res.td.w3.isoc.pkt1);
+ }
+ format = PIPE_ISOCHRONOUS;
+ }
+
+ mxc_req->etd_urb_state = ETD_URB_COMPLETED;
+
+ /* Convert completion code to HW independent value. */
+
+ if (killed) {
+ cc = -ENOENT;
+ remaining = 0;
+ }
+ else
+ cc = comp_code_to_status(cc);
+
+ /* Return ETD and X,Y buffers to free list. */
+
+ y = data_buff_addr(res.td.w1.bufsrtad.y);
+ x = data_buff_addr(res.td.w1.bufsrtad.x);
+ y = rel_data_buff(mxc_hcd, y);
+ x = rel_data_buff(mxc_hcd, x);
+
+ /* clear ep and active entries */
+
+ mxc_hcd->ep[address][mxc_req->epnum] = NULL;
+ rel_etd_irq(mxc_hcd, mxc_req->etdn);
+ mxc_hcd->active_count--;
+
+ /* Forward results to the layers above. */
+
+ UNLESS (urb) {
+ printk(KERN_INFO"%s: active_urb[%d] NULL", __FUNCTION__, mxc_req->etdn);
+ TRACE_MSG1(HCD,"active_urb[%d] NULL", mxc_req->etdn);
+ return;
+ }
+
+ TRACE_MSG5(HCD,"id: %d status: %d tlen: %d rem: %d buffer: %p",
+ mxc_req->etdn, cc, urb->transfer_buffer_length, remaining, urb->transfer_buffer);
+#if 0
+ if (urb->transfer_buffer) {
+ int i;
+ u8 *cp = urb->transfer_buffer;
+ urb->actual_length = urb->transfer_buffer_length - remaining;
+
+ TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length);
+
+ for (i = 0; i < urb->actual_length; i+= 8)
+
+ TRACE_MSG8(HCD, "RECV %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+#endif
+
+ if (-ENOENT == cc) {
+ // Cancelled.
+ urb->actual_length = 0;
+ }
+ else {
+ switch (format) {
+ case PIPE_CONTROL:
+ urb->actual_length = urb->transfer_buffer_length - remaining;
+
+ if (urb->setup_packet) {
+ u8 *cp = urb->setup_packet;
+ TRACE_MSG8(HCD, "SETUP %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ }
+
+
+ // XXX Should this be moved?
+ //
+ // XXX We need to figure out a new way to ensure first device. Perhaps active_count == 1.
+ //
+ //if ((dev == mxc_hcd->first_dev) && !cc && urb->setup_packet)
+ {
+
+ struct usb_ctrlrequest *request = (struct usb_ctrlrequest*) urb->setup_packet;
+ switch (request->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ otg_queue_event(hcd_instance->otg, ADDRESSED, HCD, "HCD COMPLETE ADDRESSED");
+ break;
+ case USB_REQ_SET_CONFIGURATION:
+ otg_queue_event(hcd_instance->otg, CONFIGURED, HCD, "HCD COMPLETE CONFIGURED");
+ break;
+ // XXX check for GET_CONFIGURATION and otg descriptor here
+ }
+ }
+ break;
+
+ case PIPE_BULK:
+ case PIPE_INTERRUPT:
+ if (0 == cc) {
+ // QQSV needed for BULK, what about interrupt?
+ TRACE_MSG4(HCD,"dev: %d ep: %d dir: %d TOGGLE: %d",urb->dev->devnum,
+ usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle);
+ usb_settoggle(urb->dev,usb_pipeendpoint(urb->pipe),usb_pipeout(urb->pipe),next_toggle);
+ }
+ urb->actual_length = urb->transfer_buffer_length - remaining;
+ //hcd_trace_mem(HCD,__FUNCTION__,"BULK/INTERRUPT urb",urb->actual_length,urb->transfer_buffer);
+ break;
+
+ case PIPE_ISOCHRONOUS:
+ urb->actual_length = urb->transfer_buffer_length - remaining; // QQSV
+ break;
+
+ default: // Paranoia.
+ urb->actual_length = 0;
+ break;
+ }
+ }
+
+ /* give the urb back to the hcd driver, put the request back into the unused queue */
+ mxc_hcd_giveback_req_irq(mxc_hcd, mxc_req, cc);
+ //list_add_tail(&mxc_req->queue, &mxc_hcd->unused);
+ return;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! MXC Root Hub Support
+ *
+ * We track all changes to the MXC root hub and push the OTG port changes through
+ * to the upper layers IFF we are in a_host or b_host states.
+ *
+ * This requires that we implement some of the root hub state machine to get ports
+ * connected etc even if we are not currently pushing up and conversly emulate
+ * the port connect changes and reset/enable for the root hub implementation in
+ * the usb core layer.
+ *
+ */
+char *port_feature_name[] = {
+ "PORT_CONNECTION", "PORT_ENABLE", "PORT_SUSPEND", "FEATURE 0x03",
+ "PORT_RESET", "FEATURE 0x05", "FEATURE 0x06", "FEATURE 0x07",
+ "PORT_POWER", "PORT_LOW_SPEED", "FEATURE 0x0a", "FEATURE 0x0b",
+ "FEATURE 0x0c", "FEATURE 0x0d", "FEATURE 0x0e", "FEATURE 0x0f",
+ "C_PORT_CONNECTION", "C_PORT_ENABLE", "C_PORT_SUSPEND", "C_PORT_OVER_CURRENT",
+ "C_PORT_RESET", "PORT_TEST",
+};
+
+/*!
+ * mxc_hcd_hw_rh_port_feature() - get rh port feature
+ * @param mxc_hcd
+ * @param wValue is feature selector
+ * @param wIndex is port number (1-N)
+ * @param set_flag
+ */
+void mxc_hcd_hw_rh_port_feature(struct mxc_hcd *mxc_hcd, u16 wValue, u16 wIndex, int set_flag)
+{
+ char buf[64];
+ sprintf(buf, "port %d feature %s %s", wIndex, port_feature_name[wValue], set_flag ? "SET" : "RESET");
+
+ if (wIndex == 1)
+ TRACE_MSG5(HCD, "-> port: %d %s (%d) %s port_stat: %8x", wIndex, port_feature_name[wValue],
+ wValue, set_flag ? "SET" : "RESET",
+ fs_rl(fs_host_port_stat(wIndex)));
+
+ RETURN_UNLESS(mxc_hcd->otg_port_enabled);
+ if (set_flag) {
+ // SET feature
+ switch (wValue) {
+ case PORT_SUSPEND:
+ otg_queue_event(hcd_instance->otg, BUS_SUSPENDED, HCD, "HUB_PORT_SUSPEND (mx21 hw)");
+ fs_wl(fs_host_port_stat(wIndex), 1 << wValue);
+ break;
+ case PORT_RESET:
+ case PORT_POWER:
+ fs_wl(fs_host_port_stat(wIndex), 1 << wValue);
+ break;
+ default:
+ // Error, but ignore.
+ TRACE_MSG2(HCD,"SET port %d invalid feature %d", wIndex, wValue);
+ break;
+ }
+ }
+ else {
+ // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423).
+ switch (wValue) {
+ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active.
+ wValue++; // yes, really. this clears PORT_SUSPEND
+ fs_wl(fs_host_port_stat(wIndex), 1 << wValue);
+ otg_queue_event(hcd_instance->otg, BUS_SUSPENDED_, HCD, "HUB_PORT_SUSPEND/ (mx21 hw)");
+ break;
+
+ case PORT_POWER: // Put port in powered-off state.
+ wValue++; // yes, really. this clears PORT_POWER
+ fs_wl(fs_host_port_stat(wIndex), 1 << wValue);
+ break;
+
+ case PORT_ENABLE: // Disable port.
+ //wValue--;
+ break;
+ default:
+ break;
+ }
+ switch (wValue) {
+ case PORT_POWER: // Put port in powered-off state.
+ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active.
+ case PORT_ENABLE: // Disable port.
+ case PORT_INDICATOR: // ind_selector gives which indicator to clear
+ case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit
+ case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit
+ case C_PORT_RESET: // clear the PORT_RESET change bit
+ case C_PORT_ENABLE: // clear the PORT_ENABLE change bit
+ case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit
+ fs_wl(fs_host_port_stat(wIndex), 1 << wValue);
+ break;
+ default:
+ // Error, but ignore.
+ TRACE_MSG2(HCD,"CLEAR port %d invalid feature %d", wIndex, wValue);
+ break;
+ }
+ }
+
+ if (wIndex == 1)
+ TRACE_MSG4(HCD, "<- port: %d %s %s port_stat: %8x", wIndex, port_feature_name[wValue], set_flag ? "SET" : "RESET",
+ fs_rl(fs_host_port_stat(wIndex)));
+}
+
+/*!
+ * MXC_PORT_CHANGED() - send otg event information to state machine
+ * @param name
+ * @param cs
+ * @param changed
+ * @param status
+ * @param set
+ * @param reset
+ */
+void MXC_PORT_CHANGED(char *name, u32 cs, u32 changed, u32 status, otg_current_t set, otg_current_t reset)
+{
+ RETURN_UNLESS(cs & changed);
+ TRACE_MSG2(HCD, "%s%s", name, (cs & (1 << status)) ? "" : "/");
+ if (cs & (1 << status) && set)
+ otg_queue_event(hcd_instance->otg, set, HCD, name);
+ if (!(cs & (1 << status)) && reset)
+ otg_queue_event(hcd_instance->otg, reset, HCD, name);
+}
+
+
+/*!
+ * mxc_hcd_update_shadow_rh() - Update shadow _status_ info (change info already updated).
+ * @param mxc_hcd
+ * @param port_num - 1-N based port number
+ * @param cs
+ *
+ * Process change status for OTG port. Track changes into virtual hub change
+ * status so that virtual root hub implementation can see them when we want
+ * them to see them.
+ *
+ * N.B. The port reset processing is done here so that the OTG state machine
+ * can oversee things. We emulate it for the upper layers when we want them
+ * to see the actual connection.
+ *
+ */
+static void mxc_hcd_update_shadow_rh(struct mxc_hcd *mxc_hcd, int port_num, u32 cs)
+{
+ TRACE_MSG2(HCD,"port: %d cs: %08x", port_num, cs);
+
+ /* PORT_CONNECTION (0) bit has changed (either direction).
+ * PORT_STATUS_CONNECTSC (16) - PORT_CONNECTION (0) changed
+ *
+ * When set will initiate a PORT_RESET, which should result in PORT_ENABLE.
+ *
+ * When reset this needs to clear virtual hub connection if otg port.
+ */
+ MXC_PORT_CHANGED("PORT_CONNECTION", cs, PORT_STATUS_CONNECTSC, PORT_CONNECTION,
+ HUB_PORT_CONNECT, HUB_PORT_CONNECT_ );
+
+ if (cs & (1 << C_PORT_CONNECTION)) {
+ if (cs & (1 << PORT_CONNECTION)) {
+ if (cs & (1 << PORT_LOW_SPEED)) TRACE_MSG1(HCD,"port: %d low speed device", port_num);
+
+ // initiate reset, which should enable port
+ mxc_hcd_hw_rh_port_feature(mxc_hcd, PORT_RESET, port_num, TRUE);
+ }
+ else {
+
+ mxc_hcd->virt_port_status[port_num - 1] &= ~(1 << PORT_CONNECTION);
+ mxc_hcd->virt_port_status[port_num - 1] &= ~(1 << PORT_ENABLE);
+ mxc_hcd->virt_port_status[port_num - 1] |= (1 << C_PORT_CONNECTION);
+ //mxc_hcd->virt_port_status[port_num - 1] |= (1 << C_PORT_ENABLE);
+ mxc_hcd->virt_hub_port_change_status |= (1 << port_num);
+
+ TRACE_MSG2(HCD,"port: %d disconnection virt_port_status: %08x",
+ port_num, mxc_hcd->virt_port_status[port_num - 1]);
+ }
+ }
+
+
+ /* PORT_RESET (4) status change, end of reset, PORT_ENABLE may (should) be on.
+ *
+ * This should be because of PORT_RESET started due to PORT_CONNECT and
+ * should have resulted in PORT_ENABLE.
+ *
+ * If PORT_ENABLE is true then we can set PORT_CONNECT in virtual hub if otg port.
+ *
+ */
+ MXC_PORT_CHANGED("PORT_RESET", cs, PORT_STATUS_PRTRSTSC, PORT_RESET, BUS_RESET, BUS_RESET_);
+ if (cs & (1 << C_PORT_RESET)) {
+ TRACE_MSG1(HCD,"port: %d PORT_RESET complete (s.b. enabled)", port_num);
+ if (cs & (1 << PORT_ENABLE)) {
+ TRACE_MSG1(HCD,"port: %d enabled", port_num);
+
+ /* If OTG Port set Port Connection in virtual root hub, this starts
+ * the sequeuence of events required to get virtual
+ * root hub to emulate a connection.
+ */
+ mxc_hcd->virt_port_status[port_num - 1] |= (1 << PORT_CONNECTION);
+ mxc_hcd->virt_port_status[port_num - 1] |= (1 << C_PORT_CONNECTION);
+ mxc_hcd->virt_hub_port_change_status |= (1 << port_num);
+
+ TRACE_MSG2(HCD,"port: %d reset virt_port_status: %08x",
+ port_num, mxc_hcd->virt_port_status[port_num - 1]);
+ }
+ else
+ TRACE_MSG1(HCD,"port: %d NOT enabled!!!", port_num);
+
+ }
+
+
+ /* PORT_ENABLE (1) has been __cleared__ by some HW event
+ * PORT_STATUS_PRTENBLSC (17) - PORT_ENABLE (1) changed
+ *
+ * This will be set only on port error.
+ *
+ * This needs to clear virtual hub connection if otg port.
+ */
+ MXC_PORT_CHANGED("PORT_ENABLE", cs, PORT_STATUS_PRTENBLSC, PORT_ENABLE, (u64) 0, (u64) 0);
+ if (cs & (1 << C_PORT_ENABLE)) {
+ TRACE_MSG1(HCD,"port: %d disabled (by HW)", port_num);
+ if ((cs & (1 << PORT_ENABLE))) {
+
+ }
+ else {
+
+ /* Reset Port Connection in virtual root hub
+ */
+ TRACE_MSG2(HCD,"port: %d lost connection virt_port_status: %08x",
+ port_num, mxc_hcd->virt_port_status[port_num - 1]);
+
+ mxc_hcd->virt_port_status[port_num - 1] &= ~(1 << PORT_CONNECTION);
+ mxc_hcd->virt_port_status[port_num - 1] &= ~(1 << PORT_ENABLE);
+ //mxc_hcd->virt_port_status[port_num - 1] |= (1 << C_PORT_CONNECTION);
+ mxc_hcd->virt_port_status[port_num - 1] |= (1 << C_PORT_ENABLE);
+ mxc_hcd->virt_hub_port_change_status |= (1 << port_num);
+
+ TRACE_MSG2(HCD,"port: %d enable lost virt_port_status: %08x",
+ port_num, mxc_hcd->virt_port_status[port_num - 1]);
+ }
+ }
+
+ /* PORT_OVER_CURRENT (3) bit has changed (either direction).
+ * PORT_STATUS_OVRCURIC (19) - PORT_OVER_CURRENT (3) changed
+ *
+ * XXX This will need to clear virtual hub.
+ */
+ MXC_PORT_CHANGED("PORT_OVER_CURRENT", cs, PORT_STATUS_OVRCURIC, PORT_OVER_CURRENT, (u64) 0, (u64) 0);
+ if (cs & (1 << C_PORT_OVER_CURRENT)) {
+ if (cs & (1 << PORT_OVER_CURRENT)) {
+ TRACE_MSG1(HCD,"port: %d over current ON", port_num);
+ }
+ else {
+ TRACE_MSG1(HCD,"port: %d over current OFF", port_num);
+ }
+ }
+
+ /* PORT_SUSPEND (2) Resume sequence has completed....
+ * PORT_STATUS_PRTSTATSC (18) - PORT_SUSPEND (2) changed
+ */
+ MXC_PORT_CHANGED("PORT_SUSPEND", cs, PORT_STATUS_PRTSTATSC, PORT_SUSPEND,
+ BUS_SUSPENDED, BUS_SUSPENDED_);
+
+ if (cs & (1 << C_PORT_SUSPEND)) {
+ // FIXME - figure this out when we get to suspend/resume....
+ }
+
+ //TRACE_MSG5(HCD, "port: %d shadow port_change_status: ((%08x & ~%04x) | %04x) hpcs: %04x",
+ // port_num, mxc_hcd->real_port_change_status[port_num - 1],
+ // mxc_hcd->real_hub_port_change_status);
+}
+
+/* ********************************************************************************************** */
+
+/*! mxc_hcd_rh_portstatus_bh()
+ *
+ * Track changes in portstatus. This is run as a bottom-half handler when
+ * changes in the port status registers are detected in the interrupt
+ * handler.
+ *
+ */
+static void mxc_hcd_rh_portstatus_bh(void *data)
+{
+ struct mxc_hcd *mxc_hcd = (struct mxc_hcd *) data;
+ u8 change_data;
+ struct urb *int_urb;
+
+ u32 real_hub_port_change_status;
+ u32 cs;
+ unsigned long flags;
+
+ int i;
+
+ TRACE_MSG3(HCD, "port_change_status: %08x %08x %08x", mxc_hcd->real_port_change_status[0],
+ mxc_hcd->real_port_change_status[1], mxc_hcd->real_port_change_status[2]);
+
+ /* check for changes in real port status */
+
+ RETURN_UNLESS (cs = mxc_hcd->real_port_change_status[0] | fs_rl(fs_host_port_stat(1))); // OTG Port
+
+ /* clear real port status and update the OTG port to the upper layers */
+
+ mxc_hcd->real_port_change_status[0] = 0;
+ mxc_hcd_update_shadow_rh(mxc_hcd, 1, cs);
+}
+
+/*! mxc_hcd_rh_int_hndler() - handle port status interrupt
+ * @param mxc_hcd
+ *
+ * This finds, saves and clears port status changes, then schedules
+ * the bottom-half handler to process them.
+ *
+ * XXX Current MXC hardware reports apparantly spurious changes to port status
+ * for the non-OTG ports. These must be properly cleared.
+ *
+ */
+void mxc_hcd_rh_int_hndlr(struct mxc_hcd *mxc_hcd)
+{
+ u32 roothub_status = fs_rl(OTG_HOST_ROOTHUB_STATUS);
+ int port_num;
+
+ TRACE_MSG1(HCD, "HOST_PSCINT ROOTHUB_STATUS: %08x", roothub_status);
+
+ if (roothub_status & ROOTHUB_STATUS_OVRCURCHG) {
+ fs_wl(OTG_HOST_ROOTHUB_STATUS, ROOTHUB_STATUS_OVRCURCHG);
+ roothub_status = fs_rl(OTG_HOST_ROOTHUB_STATUS);
+ TRACE_MSG1(HCD, "HOST_PSCINT ROOTHUB_STATUS: %08x CLEARED", roothub_status);
+ }
+
+ //printk(KERN_INFO"%s: cs: %08x %08x %08x\n", __FUNCTION__, fs_rl(fs_host_port_stat(1)),
+ // fs_rl(fs_host_port_stat(2)), fs_rl(fs_host_port_stat(3)));
+
+ /* Clear the interrupt by clearing the port(s) in question.
+ * Reflect any changes in the shadow values.
+ */
+ for (port_num = 1; port_num <= mxc_hcd->bNbrPorts; port_num++) {
+
+ u32 cs = fs_rl(fs_host_port_stat(port_num));
+ u32 cm = cs & 0xFFFF0000;
+
+ CONTINUE_UNLESS (cm);
+ if (port_num == 1)
+ TRACE_MSG3(HCD, "port: %d cs: %08x cm: %08x", port_num, cs, cm);
+
+ if (port_num == 1) {
+ mxc_hcd->real_hub_port_change_status |= (1 << port_num);
+ mxc_hcd->real_port_change_status[port_num - 1] |= cm;
+ }
+ if ( (fs_rl(OTG_HOST_ROOTHUB_STATUS) & 0x2) &&
+ (cs & 0x8) ) //Over current in port and hub occurs
+ {
+ printk (KERN_INFO"USB port overcurrent is happened !?\n");
+ }
+
+ /* clear port status */
+ if (cm) {
+ //printk(KERN_INFO"%s: clearing[%d] %08x\n", __FUNCTION__, port_num, cm);
+ fs_wl(fs_host_port_stat(port_num), cm);
+ cs = fs_rl(fs_host_port_stat(port_num));
+ cm = cs & 0xFFFF0000;
+ //printk(KERN_INFO"%s: port[%d] cs: %08x cm: %08x CLEARED\n", __FUNCTION__, port_num, cs, cm);
+ TRACE_MSG3(HCD, "port: %d cs: %08x cm: %08x CLEARED", port_num, cs, cm);
+ }
+ }
+
+ TRACE_MSG3(HCD, "port_change_status: %08x %08x %08x", mxc_hcd->real_port_change_status[0],
+ mxc_hcd->real_port_change_status[1], mxc_hcd->real_port_change_status[2]);
+
+ /* Tell the RH bottom-half to scan for changes in shadow data. (schedule bottom half handler)
+ */
+ PREPARE_WORK_ITEM(mxc_hcd->rh_bh, mxc_hcd_rh_portstatus_bh, mxc_hcd);
+ SCHEDULE_WORK(mxc_hcd->rh_bh);
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! MXC Host Interrupt Handler
+ *
+ * Handle a host controller interrupt. This can be for either a transfer complete
+ * or root hub / port change etc.
+ */
+
+static int num_host_interrupts = 0;
+#define MAX_HOST_INTERRUPTS 0
+#define MAX_PSC_INTERRUPTS 0
+
+/*!
+ * mxc_hcd_hw_int_hndlr() - interrupt handler for hcd controller
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+
+
+
+
+irqreturn_t mxc_hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct mxc_hcd *mxc_hcd = hcd_instance->privdata; // XXX this should come from dev_id
+
+ u32 host_sint; // C.f. 23.11.11 Host Interrupt Register
+
+ int loop_count = 0;
+ struct usb_hcd *hcd = (struct usb_hcd *) mxc_hcd;
+ int start = hcd->state;
+
+
+ /* XXX - what is this.... */
+ if (OTG_USBDMA == irq) {
+ // HOST DMA error interrupt
+ u32 err_stat = fs_rl(OTG_DMA_ETD_ERR);
+ TRACE_MSG1(HCD,"host DMA interrupt, error status %08x",err_stat);
+ if (0 != err_stat) {
+ // Disable the matching DMA channels and clear the interrupt
+ fs_wl(OTG_DMA_ETD_CH_CLR, err_stat);
+ fs_wl(OTG_DMA_ETD_ERR, err_stat);
+ }
+ // local_irq_restore(flags);
+ return IRQ_HANDLED;
+ }
+
+ /* FIXME - should check mxc_hcd->mm->otg.OTG_Module_Interrupt_Status & (0x1 << 3) | 0x1;
+ * for Host (async + regular) Interrupt - enable clock on async.
+ */
+#if 1
+ if (unlikely(start == HC_STATE_HALT ||
+ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ return IRQ_NONE;
+#endif
+ while ((host_sint = fs_rl(OTG_HOST_SINT_STAT) & mxc_hcd->int_mask)) {
+
+ num_host_interrupts++;
+ if ((loop_count++ > 100) || (MAX_HOST_INTERRUPTS && (num_host_interrupts > MAX_HOST_INTERRUPTS))) {
+ printk(KERN_INFO"%s: DISABLED\n", __FUNCTION__);
+ fs_wl(OTG_HOST_SINT_STEN, 0);
+ fs_andl(OTG_CORE_CINT_STEN, ~(/*(0x1 << 3) |*/ 0x1));
+ return IRQ_HANDLED;
+ }
+
+ TRACE_MSG1(HCD, "host_sint: %08x", host_sint);
+
+ while (HOST_DONEINT & host_sint) {
+ u32 etds_done;
+
+ BREAK_IF(loop_count++ > 100);
+
+ TRACE_MSG1(HCD,"HOST_DONEINT: %08x", fs_rl(OTG_HOST_EP_DSTAT));
+ fs_wl(OTG_HOST_SINT_STAT, HOST_DONEINT);
+ host_sint = fs_rl(OTG_HOST_SINT_STAT) & mxc_hcd->int_mask;
+
+ /* while DSTAT is non-zero call finish urb on highest bit set
+ */
+ while ((etds_done = fs_rl(OTG_HOST_EP_DSTAT))) {
+ int etdn = fls(etds_done) - 1;
+ struct mxc_req *mxc_req;
+
+ mxc_req = mxc_hcd->active[etdn];
+
+ BREAK_IF(loop_count++ > 100);
+
+ UNLESS(mxc_req) {
+ printk(KERN_INFO"%s: ERROR NO REQUEST %d etds_done: %08x etdn: %d\n",
+ __FUNCTION__, num_host_interrupts, etds_done, fls(etds_done) - 1);
+ fs_wl(OTG_HOST_EP_DSTAT, ETD_MASK(etdn));
+ continue;
+ }
+ mxc_hcd_finish_req_irq(mxc_hcd, mxc_req, mxc_req->urb, FALSE);
+ }
+ /* assuming something was done, we may be able to start
+ * something if there is anything to start...
+ */
+ mxc_hcd_schedule_irq(mxc_hcd);
+ }
+
+ host_sint = fs_rl(OTG_HOST_SINT_STAT) & mxc_hcd->int_mask;
+
+ /* Start Of Frame.
+ * Note: this interrupt seems to happen even if disabled when there is a frame number overflow.
+ */
+ if (host_sint & HOST_SOFINT) {
+
+ mxc_hcd->sof_count += 1;
+
+ TRACE_MSG2(HCD, "HOST_SOFINT: %d sof_count: %d", fs_rl(OTG_HOST_FRM_NUM), mxc_hcd->sof_count);
+
+ if (0 == (mxc_hcd->sof_count & (4096 - 1))) {
+ TRACE_MSG1(HCD, "SOF %08lx", mxc_hcd->sof_count);
+ }
+ }
+
+ /* Frame Number Overflow.
+ */
+ if (host_sint & HOST_FMOFINT)
+ TRACE_MSG0(HCD,"Frame Number Overflow");
+
+ /* Port Status Change.
+ */
+ if (host_sint & HOST_PSCINT)
+ mxc_hcd_rh_int_hndlr(mxc_hcd);
+
+ /* Overrun
+ */
+ if (host_sint & (HOST_SORINT | HOST_HERRINT)) {
+ if (host_sint & HOST_SORINT)
+ TRACE_MSG0(HCD,"Scheduling Overrun");
+ if (host_sint & HOST_HERRINT)
+ TRACE_MSG0(HCD,"Host Scheduling Error");
+ }
+ /* Resume Detected.
+ */
+ if (host_sint & HOST_RESDETINT)
+ TRACE_MSG0(HCD,"Resume Detected");
+
+ /* Clear by writing back the ones we've checked for since the last read.
+ */
+ fs_wl(OTG_HOST_SINT_STAT, host_sint & mxc_hcd->int_mask);
+ }
+#if 1
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (unlikely(hcd->state == HC_STATE_HALT))
+ usb_hc_died (hcd);
+#endif
+
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/otg/hardware/mxc-hcd.h b/drivers/otg/hardware/mxc-hcd.h
new file mode 100644
index 000000000000..396bc8f18439
--- /dev/null
+++ b/drivers/otg/hardware/mxc-hcd.h
@@ -0,0 +1,294 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-hcd.h - Freescale USBOTG aware Host Controller Driver (HCD)
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/mxc/mxc-hcd.h|20070614183949|39353
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ * Tony Tang <tt@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc-hcd.h
+ * @brief Freescale USB Host Controller Driver
+ *
+ * This is a complete and self contained Linux 2.6 USB Host Driver.
+ *
+ * It also conforms to the requirements for use as an OTG HCD driver
+ * in the Belcarra OTG Stack.
+ *
+ * The hardware is an integrated design, so it also works in conjunction
+ * with the mxc OCD and PCD drivers to share the hardware under the direction
+ * of the OTG State Machine.
+ *
+ *
+ * @ingroup FSOTG
+ */
+
+/* ********************************************************************************************* */
+
+/*! Data Structures Overview
+ *
+ * Urbs point at:
+ *
+ * struct urb {
+ * struct usb_device *udev;
+ * }
+ *
+ * Udev points at hdev (stuct hcd_dev) via:
+ *
+ * struct usb_device {
+ * void *hcpriv;
+ * }
+ *
+ * Hdev is a simple structure for each "device" on the bus that the host has
+ * enumerated. It contains an array of void *ep[32] which contains pointers
+ * to active requests indexed using the endpoint number and direction. This
+ * allows us to find an active request using endpoint information provided
+ * by the hcd layer:
+ *
+ * struct hcd_dev { // usb_device.hcpriv points to this
+ *
+ * struct list_head dev_list; // on this hcd
+ * struct list_head urb_list; // pending on this dev
+ * void *ep[32]; // per-configuration hc/hcd state such as qh or ed
+ * }
+ *
+ *
+ * The host drivers are not allowed to use the urb->urb_list so we will keep
+ * our own list of inactive requests using the mxc_req structure. Note that
+ * we track the epnum (derived from endpoint information provided by the
+ * hcd layer) and etdn (the hardware slot number) for the request if it
+ * is schedule:
+ *
+ * struct mxc_req {
+ * struct urb *urb;
+ * struct list_head queue;
+ *
+ * int epnum;
+ * int etdn;
+ * };
+ *
+ *
+ *
+ * The mxc_hcd structure contains:
+ *
+ * struct mxc_hcd {
+ * struct list_head unused; // unused requests
+ * struct list_head inactive; // in-active requests
+ * struct mxc_req *active[NUM_ETDS]; // active requests by ETD
+ * };
+ *
+ * Requests are allocated on the fly when needed, but saved in the unused
+ * queue when finished.
+ *
+ * Requests that are pending are in the inactive queue. The scheduler will
+ * attempt to start any inactive requests when new requests are queued or
+ * old requests are finished.
+ *
+ * Requests can stay on the inactive queue for two reasons, first if there is already
+ * an active request for that device and endpoint (by verifying ep[] list is null) or
+ * if there are no available resources (x/y buffers or etd slots.)
+ *
+ * The mxc_hcd structure will have a list of NUM_ETD pointer to active
+ * requests. If the array is NULL then the ETD is not being used. The active
+ * array is indexed by the ETD (hardware) number that the request is
+ * scheduled on.
+ *
+ *
+ * The primary reason for the inactive queue is that while the TDI design
+ * handles all of the details about a transfer until it is complete, it can
+ * only handle a limited number (NUM_ETDS) of requests at a time. So if there
+ * are more requests to valid device/endpoint combinations than there are ETD's
+ * then some will have to wait.
+ *
+ * Generally the maximum number of ETD's that can be used for any device is 1 (for the
+ * control endpoint) plus the number of endpoints defined in the device's configuration
+ * descriptor.
+ *
+ * It may be necessary to de-activate previously started requests periodically
+ * if there is more work than can be handled. This will require three things:
+ *
+ * - setup of etd registers in mxc request
+ * - start mxc request based on etd in mxc request
+ * - code to de-activate by saving etd in mxc request and place at end of inactive list
+ *
+ * IFF we can safely stop the ETD then the above would safely get it restarted.
+ *
+ * It may be safest to stop ETD's from SOF interrupt when (presumably) there is
+ * no active tranfers.
+ *
+ */
+
+/*! Driver Overview
+ *
+ * Data Structures
+ * ETD Management
+ * Transfer Request Management
+ * Data Buffer Management
+ * Data Tranfer Handling
+ * Real Root Hub Support
+ * MXC Host Interrupt Handler
+ * OTG Support Functions
+ * Linux 2.6 USB Device Support
+ * Linux 2.6 Device Driver Support
+ * Module init/exit
+ *
+ */
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! Data Structures
+ */
+
+
+/*!
+ * @struct fs_data_buff
+ */
+typedef struct fs_data_buff {
+ u32 data[DATA_BUFF_SIZE/sizeof(u32)]; // Cannot safely write less than 32-bit quantities
+} volatile fs_data_buff;
+
+
+/*!
+ * @struct mxc_req
+ *
+ * This is used to queue urb's so that they can be performed when there
+ * are hardware resources available.
+ *
+ */
+struct mxc_req {
+
+ struct urb *urb; // the urb for this request
+ struct list_head queue; // for inactive or unused queue
+
+ int epnum; // array index for ep and active arrays
+ int etdn; // if scheduled this is the etd we are using
+ u8 etd_urb_state; // current state
+
+ u32 remaining;
+
+ endpoint_transfer_descriptor sdp_etd; // Setup data phase save area
+ fs_data_buff *x;
+ fs_data_buff *y;
+
+ u32 setup_dma;
+ u32 transfer_dma;
+
+ void *bounce_buffer;
+ void *bounce_addr;
+ void *transfer_buffer_save;
+};
+
+
+/*! mxc_hcd
+ */
+#define MAX_HUB_PORTS 8
+#define MXC_MAX_USB_ADDRESS 8
+//#define OTG_PORT 2
+struct mxc_hcd {
+ struct usb_hcd hcd;
+ spinlock_t lock;
+
+ struct device *dev;
+
+ //u32 port1;
+ //u8 ctrl1;
+ //u8 ctrl2;
+
+ //u8 otg_device_mask;
+ BOOL otg_port_enabled;
+
+ u32 real_hub_port_change_status;
+ u32 real_port_change_status[MAX_HUB_PORTS];
+ u32 virt_hub_port_change_status;
+ u32 virt_port_status[MAX_HUB_PORTS];
+
+ u32 change_data;
+
+ struct OLD_WORK_STRUCT rh_bh;
+
+ BOOL suspended;
+
+ u32 sof_count;
+ u32 int_mask;
+
+ //int free_etds;
+
+ u16 free_buffs;
+ u16 buff_list[NUM_DATA_BUFFS];
+
+ u32 root_hub_desc_a;
+ u32 root_hub_desc_b;
+ u8 bNbrPorts;
+ u8 wHubCharacteristics;
+ u8 bPwrOn2PwrGood;
+ u8 DeviceRemovable;
+ u8 PortPwrCtrlMask;
+
+ struct list_head unused; // unused requests
+ struct list_head inactive; // in-active requests
+ struct mxc_req *active[NUM_ETDS]; // active requests by ETD
+
+ // Statistics
+ int active_count; // number of active requests
+ int allocated_count; // number of allocated request structs
+ u32 request_count; // total number of enqueued requests
+
+ //struct usb_device *first_dev;
+ void *ep[MXC_MAX_USB_ADDRESS + 1][32];
+};
+
+#define OUT(o,e)((o && e) ? 1 : 0)
+
+#define EPQ_EMPTY 0
+#define EPQ_RUNNING 1
+#define EPQ_WAITING 2
+#define EPQ_NOTUSED 3
+
+struct usbp_hub_descriptor {
+ __u8 bDescLength;
+ __u8 bDescriptorType;
+ __u8 bNbrPorts;
+ __u16 wHubCharacteristics;
+ __u8 bPwrOn2PwrGood;
+ __u8 bHubContrCurrent;
+ /* add 1 bit for hub status change; round to bytes */
+ __u8 DeviceRemovable;
+ __u8 PortPwrCtrlMask;
+} __attribute__ ((packed));
+
+
+
+static inline struct mxc_hcd *hcd_to_mxc(struct usb_hcd *hcd)
+{
+ return container_of(hcd, struct mxc_hcd, hcd);
+}
+
+/* convert epnum and out flag into index into 32 entry array */
+
+#define EPNUM(e,o) (((e & 0xf) << 1) + o)
+
+#define ETD_URB_COMPLETED 0
+#define ETD_URB_SETUP_STATUS 1
+#define ETD_URB_SETUP_DATA 2
+#define ETD_URB_SETUP_START 3
+#define ETD_URB_BULK_START 4
+#define ETD_URB_BULKWZLP 5
+#define ETD_URB_BULKWZLP_START 6
+#define ETD_URB_INTERRUPT_START 7
+#define ETD_URB_ISOC_START 8
diff --git a/drivers/otg/hardware/mxc-hrt.c b/drivers/otg/hardware/mxc-hrt.c
new file mode 100644
index 000000000000..fc642ea397bc
--- /dev/null
+++ b/drivers/otg/hardware/mxc-hrt.c
@@ -0,0 +1,174 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-hrt.c -- Freescale High Resolution timer
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/mxc/mxc-hrt.c|20070614183950|63070
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ * Shahrad Payandeh <sp@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/hardware/mxc-hrt.c
+ * @brief Freecale GPT Timer implementation.
+ *
+ * Use the Linux High Resolution Timers to implement OTG Timer.
+ *
+ * @ingroup FSOTG
+ * @ingroup LINUXOS
+ * @ingroup OCD
+ *
+ */
+
+#include <otg/pcd-include.h>
+
+#if defined (CONFIG_OTG_HRT) || defined(_OTG_DOXYGEN)
+
+#include <linux/pci.h>
+#include <linux/hrtimer.h>
+#include <asm/arch/gpio.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+/* ********************************************************************************************* */
+static struct hrtimer g_mxc_hrt_timer; /*hrt timer */
+static struct otg_instance *g_otg; /* save the OTG instance */
+static otg_tick_t g_mxc_hrt_divisor = 0;
+static otg_tick_t g_mxc_hrt_multiplier = 0;
+static BOOL g_mxc_hr_active = FALSE;
+
+/*!
+ * mxc_hrt_callback() - called to queue an a timer event to otg queue
+ * @param arg - pointer of hrtimer type
+ */
+static int mxc_hrt_callback(struct hrtimer *arg)
+{
+ TRACE_MSG1(g_otg->ocd->TAG, "checking active: %d", g_mxc_hr_active);
+ if (!g_mxc_hr_active) {
+ return HRTIMER_NORESTART;
+ }
+
+ g_mxc_hr_active = FALSE;
+ TRACE_MSG1(g_otg->ocd->TAG, "resetting active: %d", g_mxc_hr_active);
+
+ otg_queue_event(g_otg, TMOUT, g_otg->ocd->TAG, "TMOUT");
+ return HRTIMER_NORESTART;
+}
+
+/*!
+ * mxc_hrt_start_timer() - start a timer for otg state machine
+ * Set or reset timer to interrupt in number of uS (micro-seconds).
+ *
+ * XXX There may be a floor or minimum that can be effectively set.
+ * XXX We have seen an occasional problem with US(25) for discharge for example.
+ *
+ * @param otg
+ * @param usec
+ * @return 0 on success
+ */
+int mxc_hrt_start_timer(struct otg_instance *otg, int usec)
+{
+ ktime_t expires;
+
+ TRACE_MSG1(otg->ocd->TAG, "usec: %d", usec);
+
+ g_mxc_hr_active = FALSE;
+ TRACE_MSG1(otg->ocd->TAG, "resetting active: %d", g_mxc_hr_active);
+
+ hrtimer_cancel(&g_mxc_hrt_timer);
+
+ if (usec == 0) {
+ goto cancel;
+ }
+
+ g_mxc_hr_active = TRUE;
+ TRACE_MSG1(otg->ocd->TAG, "setting active: %d", g_mxc_hr_active);
+
+ if (usec < 100) {
+ TRACE_MSG1(otg->ocd->TAG, "usec: %d set to minimum 100", usec);
+ usec = 100;
+ }
+ hrtimer_init(&g_mxc_hrt_timer, CLOCK_REALTIME, HRTIMER_REL);
+ g_mxc_hrt_timer.function = mxc_hrt_callback;
+ expires = ktime_set(usec / 1000000, (usec % 1000000) * 1000);
+
+ hrtimer_start(&g_mxc_hrt_timer, expires, HRTIMER_REL);
+
+ cancel:
+ return 0;
+}
+
+/*!
+ * mxc_hrt_trace_ticks() - get current ticks
+ * GPT3 is setup as free running clock at 266 Mhz - 1/266 = .0037
+ */
+otg_tick_t mxc_hrt_trace_ticks(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ return ((tv.tv_sec * USEC_PER_SEC) + tv.tv_usec);
+}
+
+/*!
+ * mxc_hrt_trace_elapsed() - return micro-seconds between two tick values
+ *
+ * GPT3 is setup as free running clock at 266 Mhz - 1/266 = .003759
+ *
+ * MPLL = 266, FCLK = 266, BCLK = 88
+ *
+ */
+otg_tick_t mxc_hrt_trace_elapsed(otg_tick_t * t1, otg_tick_t * t2)
+{
+ otg_tick_t ticks = (*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1);
+ return ticks;
+}
+
+/*!
+ * mxc_hrt_ocd_mod_init() - initial tcd setup
+ * Allocate interrupts and setup hardware.
+ * @param otg
+ * @param divisor
+ * @param multiplier
+ * @return 0 for finish, or other on error
+ */
+int mxc_hrt_mod_init(struct otg_instance *otg, int divisor, int multiplier)
+{
+ g_mxc_hrt_divisor = divisor;
+ g_mxc_hrt_multiplier = multiplier;
+
+ hrtimer_init(&g_mxc_hrt_timer, CLOCK_REALTIME, HRTIMER_ABS);
+ g_mxc_hrt_timer.function = mxc_hrt_callback;
+ g_otg = otg;
+
+ return 0;
+}
+
+/*!
+ * mxc_hrt_mod_exit() - de-initialize
+ */
+void mxc_hrt_mod_exit(void)
+{
+ hrtimer_cancel(&g_mxc_hrt_timer);
+}
+
+#endif //CONFIG_OTG_HRT
diff --git a/drivers/otg/hardware/mxc-l26.c b/drivers/otg/hardware/mxc-l26.c
new file mode 100644
index 000000000000..edf6872b53da
--- /dev/null
+++ b/drivers/otg/hardware/mxc-l26.c
@@ -0,0 +1,1356 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-l26.c - Linux 2.6 Freescale USBOTG aware Host Controller Driver (HCD)
+ * @(#) tt/root@belcarra.com/debian286.bbb|otg/platform/mxc/mxc-l26.c|20070907214828|35671
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ * Bruce Balden <balden@belcarra.com>
+ * Tony Tang <tt@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc-l26.c
+ * @brief Freescale USB Host Controller Driver
+ *
+ * This is a complete and self contained Linux 2.6 USB Host Driver.
+ *
+ * It also conforms to the requirements for use as an OTG HCD driver
+ * in the Belcarra OTG Stack.
+ *
+ * The hardware is an integrated design, so it also works in conjunction
+ * with the mxc OCD and PCD drivers to share the hardware under the direction
+ * of the OTG State Machine.
+ *
+ *
+ * @ingroup FSOTG
+ * @ingroup LINUXOS
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+
+#include <core/hcd.h>
+
+#include <otg/usbp-hub.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-utils.h>
+#include <otg/otg-tcd.h>
+
+#include <otg/otg-hcd.h>
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include <asm/memory.h>
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/platform_device.h>
+
+#include "mxc-hcd.h"
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+extern struct usb_operations usb_hcd_operations;
+extern void mxc_hcd_schedule_irq(struct mxc_hcd *mxc_hcd);
+extern void mxc_hcd_finish_req_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req, struct urb *urb, int killed);
+extern void mxc_host_clock_on(void);
+extern void mxc_host_clock_off(void);
+extern fs_data_buff *rel_data_buff(struct mxc_hcd *mxc_hcd, fs_data_buff *db);
+extern void rel_etd_irq(struct mxc_hcd *mxc_hcd, int etdn);
+extern void mxc_hcd_hw_rh_port_feature(struct mxc_hcd *mxc_hcd, u16 wValue, u16 wIndex, int set_flag);
+extern char *port_feature_name[];
+int mxc_host_gpio (void);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! Transfer Request Management
+ */
+
+/*! mxc_get_request - get unused mxc_request or allocate a new one
+ * @param mxc_hcd
+ * @return requested mxc_req
+ */
+struct mxc_req * mxc_get_request(struct mxc_hcd *mxc_hcd)
+{
+ struct mxc_req *mxc_req = NULL;
+ struct mxc_req *mxc_req_tmp = NULL;
+ unsigned long flags;
+
+ /* attempt to find first entry in list */
+
+ local_irq_save(flags);
+ list_for_each_entry(mxc_req_tmp, &mxc_hcd->unused, queue) {
+ /* delete from list */
+ mxc_req = mxc_req_tmp;
+ list_del(&mxc_req->queue);
+ break;
+ }
+ local_irq_restore(flags);
+
+ /* otherwise attempt to allocate and initialize */
+
+ UNLESS(mxc_req) {
+ RETURN_NULL_UNLESS((mxc_req = CKMALLOC(sizeof(struct mxc_req))));
+ INIT_LIST_HEAD(&mxc_req->queue);
+ local_irq_save(flags);
+ mxc_hcd->allocated_count++;
+ local_irq_restore(flags);
+ }
+ return mxc_req;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! mxc_hcd_urq_enqueue - enqueue an mxc request
+ * @param hcd
+ * @param ep
+ * @param urb
+ * @param mem_flags
+ *
+ * New Style L2.6.10 USB Core urb enqueue.
+ */
+int mxc_hcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep, struct urb *urb, gfp_t mem_flags)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ struct usb_device *udev = urb->dev;
+ //struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv;
+
+ //int dev_addr = usb_pipedevice(urb->pipe);
+ unsigned int pipe = urb->pipe;
+ int is_out = usb_pipein(pipe) ? 0 : 1;
+ int type = usb_pipetype(pipe);
+ int endpoint = usb_pipeendpoint(pipe);
+ unsigned long flags;
+ int address = usb_pipedevice(urb->pipe) ? (usb_pipedevice(urb->pipe) % MXC_MAX_USB_ADDRESS + 1) : 0;
+
+ struct mxc_req *mxc_req = NULL;
+
+ TRACE_MSG5(HCD, "hcd: %x urb: %x endpoint: %02x is_out: %d type: %0d", hcd, urb, endpoint, is_out, type);
+
+ /* data address needs to be on a 32-bit boundary. */
+
+ RETURN_EPIPE_IF (0x3 & (u32)urb->transfer_buffer);
+
+ /* get request */
+
+ RETURN_ENOMEM_UNLESS((mxc_req = mxc_get_request(mxc_hcd)));
+
+ /* initialize request */
+
+ mxc_req->urb = urb;
+ mxc_req->etdn = -1;
+ mxc_req->etd_urb_state = EPQ_NOTUSED;
+ mxc_req->remaining = 0;
+ memset((void *)&mxc_req->sdp_etd, 0, sizeof(mxc_req->sdp_etd));
+ mxc_req->setup_dma = mxc_req->transfer_dma = 0;
+ urb->hcpriv = mxc_req;
+ ep->hcpriv = (void *) pipe;
+
+ /* XXX MXC DMA can only transfer full buffers, so we need to provide
+ * an intermediate buffer for it to use.
+ */
+ mxc_req->bounce_buffer = NULL;
+ if (!is_out && (urb->transfer_buffer_length % 128)) {
+
+ int length = ((urb->transfer_buffer_length + 128) / 128) * 128;
+
+ TRACE_MSG2(HCD, "bounce buffer length: %d -> %d", urb->transfer_buffer_length, length);
+
+ UNLESS((mxc_req->bounce_buffer = CKMALLOC(length + 32))) {
+ local_irq_save(flags);
+ list_add_tail(&mxc_req->queue, &mxc_hcd->unused);
+ local_irq_restore(flags);
+ spin_unlock(&urb->lock);
+ return -ENOMEM;
+ }
+
+ mxc_req->transfer_buffer_save = urb->transfer_buffer;
+ mxc_req->bounce_addr = mxc_req->bounce_buffer;
+ urb->transfer_buffer = mxc_req->bounce_addr;
+
+ //if (is_out)
+ // memcpy(mxc_req->bounce_addr, mxc_req->transfer_buffer_save, urb->transfer_buffer_length);
+ }
+
+ /* map setup_packet and transfer_buffer */
+
+ if (urb->setup_packet)
+ mxc_req->setup_dma = dma_map_single ( mxc_hcd->hcd.self.controller,
+ urb->setup_packet, sizeof (struct usb_ctrlrequest), DMA_TO_DEVICE);
+
+ if (urb->transfer_buffer && urb->transfer_buffer_length)
+ mxc_req->transfer_dma = dma_map_single ( mxc_hcd->hcd.self.controller,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+
+
+ TRACE_MSG5(HCD, "setup_packet: %x %x transfer_buffer: %x %x %d",
+ urb->setup_packet, mxc_req->setup_dma,
+ urb->transfer_buffer, mxc_req->transfer_dma,
+ urb->transfer_buffer_length);
+
+ /* in case of unlink-during-submit */
+
+ spin_lock(&urb->lock);
+
+ if (urb->status != -EINPROGRESS) {
+ printk(KERN_INFO"%s: EINPROGRESS\n", __FUNCTION__);
+ TRACE_MSG2(HCD, "status != EINPROGRESS hcd: %x urb: %x", hcd, urb);
+ local_irq_save(flags);
+ list_add_tail(&mxc_req->queue, &mxc_hcd->unused);
+ local_irq_restore(flags);
+ spin_unlock(&urb->lock);
+ return -ENOMEM;
+ }
+ spin_unlock(&urb->lock);
+
+ local_irq_save(flags);
+ mxc_hcd->request_count++;
+ list_add_tail(&mxc_req->queue, &mxc_hcd->inactive);
+ local_irq_restore(flags);
+
+ /* if ep[EPNUM(endpoint,is_out)] is NULL then nothing is currently scheduled
+ * for this device to that endpoint
+ */
+ UNLESS (mxc_hcd->ep[address][EPNUM(endpoint, is_out)]) {
+ TRACE_MSG2(HCD, "SCHEDULING hcd: %x urb: %x", hcd, urb);
+ local_irq_save(flags);
+ mxc_hcd_schedule_irq(mxc_hcd);
+ local_irq_restore(flags);
+ }
+ return 0;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! mxc_hcd_giveback_req - give an urb back to the hcd driver
+ * @param mxc_hcd
+ * @param mxc_req
+ * @param status
+ *
+ * This function will set the status of an urb IFF it is currently EINPROGRESS
+ * and then use usb_hcd_giveback_req() to pass control of the urb back to the
+ * hcd driver.
+ */
+void mxc_hcd_giveback_req_irq(struct mxc_hcd *mxc_hcd, struct mxc_req *mxc_req, int status)
+{
+ struct urb *urb = mxc_req->urb;
+ unsigned int pipe = urb->pipe;
+ int is_out = usb_pipeout(pipe);
+
+ TRACE_MSG2(HCD, "urb: %x status: %x", mxc_req->urb, status);
+
+ /* unmap setup_packet and transfer_buffer */
+
+ if (urb->setup_packet)
+ dma_unmap_single (mxc_hcd->hcd.self.controller, mxc_req->setup_dma,
+ sizeof (struct usb_ctrlrequest), DMA_TO_DEVICE);
+
+ if (urb->transfer_buffer && urb->transfer_buffer_length)
+ dma_unmap_single (mxc_hcd->hcd.self.controller,
+ mxc_req->transfer_dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+
+ if (mxc_req->bounce_buffer) {
+
+ if (!is_out)
+ memcpy(mxc_req->transfer_buffer_save, mxc_req->bounce_addr, urb->transfer_buffer_length);
+
+ urb->transfer_buffer = mxc_req->transfer_buffer_save;
+ LKFREE(mxc_req->bounce_buffer);
+ mxc_req->bounce_buffer = NULL;
+ }
+
+#if 0
+ if (urb->transfer_buffer) {
+ int i;
+ u8 *cp = urb->transfer_buffer;
+
+ TRACE_MSG1(HCD, "NEXT TX: length: %d", urb->actual_length);
+
+ for (i = 0; i < urb->actual_length; i+= 8)
+
+ TRACE_MSG8(HCD, "BUF: %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+#endif
+
+
+ /* Call the upper layer completion routine. Decrement the ref count
+ * (release our interest in this urb).
+ */
+ if (urb->status == -EINPROGRESS) urb->status = status;
+
+ urb->hcpriv = NULL;
+ usb_hcd_giveback_urb(&mxc_hcd->hcd, mxc_req->urb, NULL /* XXX */);
+ list_add_tail(&mxc_req->queue, &mxc_hcd->unused);
+}
+
+
+/*! mxc_hcd_urb_dequeue -
+ * @param hcd
+ * @param urb
+ * @return error if not found
+ *
+ * Used by hcd driver to dequeue a previously enqueued urb that has not
+ * completed.
+ *
+ * This should find and stop the request and then use the giveback
+ * procedure to pass control of the urb back to the hcd layer.
+ */
+int mxc_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ struct mxc_req *mxc_req;
+ struct mxc_req *mxc_req_save;
+
+ unsigned long flags;
+ int etdn;
+
+ unsigned int pipe = urb->pipe;
+ int is_out = usb_pipeout(pipe);
+ int type = usb_pipetype(pipe);
+ int endpoint = usb_pipeendpoint(pipe);
+
+ TRACE_MSG5(HCD, "hcd: %x urb: %x endpoint: %02x is_out: %d type: %0d", hcd, urb, endpoint, is_out, type);
+
+ // XXX
+ // mxc_req = urb->hcpriv;
+
+ local_irq_save(flags);
+
+ /* search through inactive list */
+
+ list_for_each_entry_safe(mxc_req, mxc_req_save, &mxc_hcd->inactive, queue) {
+ CONTINUE_IF(mxc_req->urb != urb);
+
+ TRACE_MSG1(HCD, "urb: %x found inactive", urb);
+
+ /* found it - delete from active list, restore irq, giveback and return */
+
+ list_del(&mxc_req->queue);
+ mxc_hcd_giveback_req_irq(mxc_hcd, mxc_req, 0); // check if 0 is correct status to return
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ /* search through active array */
+
+ for (etdn = 0; etdn < NUM_ETDS; etdn++) {
+ mxc_req = mxc_hcd->active[etdn];
+ CONTINUE_UNLESS(mxc_req);
+ CONTINUE_IF(mxc_req->urb != urb);
+
+ /* found it - finish it (and giveback), restore irq and return */
+ TRACE_MSG1(HCD, "urb: %x found active", urb);
+
+ mxc_hcd_finish_req_irq(mxc_hcd, mxc_req, mxc_req->urb, TRUE); // XXX This should work - need to test
+ local_irq_restore(flags);
+ return 0;
+ }
+
+
+ /* didn't find it - restore irq, return error */
+
+ TRACE_MSG1(HCD, "urb: %x not found", urb);
+ local_irq_restore(flags);
+
+ return -EINVAL;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*! OTG Support Functions
+ */
+
+/*! mxc_hcd_bh_init - MXC Hardware bottom half and OTG Init
+ * Called as a "bottom-half" to do usbcore registration once all the HW is usable.
+ * @param arg - hacd instance
+ */
+static void mxc_hcd_bh_init(void *arg)
+{
+ struct hcd_instance *hcd = (struct hcd_instance *) arg;
+ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT SET HCD_OK");
+ TRACE_MSG0(HCD,"finished");
+}
+
+static void mxc_hcd_bh_exit(void *arg)
+{
+ struct hcd_instance *hcd = (struct hcd_instance *) arg;
+ otg_event(hcd->otg, HCD_OK, HCD, "HCD_INIT RESET (EXIT) HCD_OK");
+}
+
+
+/*! hcd_init_func - per host controller common initialization
+ *
+ * This is called to initialize / de-initialize the HCD, all except the last
+ * stage of registering the root hub, because that needs to wait until rh_hcd_en_func()
+ *
+ * We start work items to do this.
+ * @param otg - otg instance
+ * @param flag - initialize /de-initialize flag
+ *
+ */
+void hcd_init_func (struct otg_instance *otg, u8 flag)
+{
+ struct hcd_instance *hcd = otg->hcd;
+ struct mxc_hcd *mxc_hcd = hcd ? hcd->privdata : NULL;
+
+ switch (flag) {
+ case SET:
+ // Schedule BH for mxc_hcd_bh_init...
+ TRACE_MSG0(HCD, "HCD_INIT: SET");
+ //PREPARE_WORK_ITEM(hcd->bh, mxc_hcd_bh_init, hcd);
+ //SCHEDULE_WORK(hcd->bh);
+ mxc_hcd_bh_init((void *)hcd);
+ TRACE_MSG0(HCD, "mxc_hcd_bh_init() schedule finished");
+ break;
+
+ case RESET:
+ TRACE_MSG0(HCD, "HCD_INIT: RESET");
+ //PREPARE_WORK_ITEM(hcd->bh, mxc_hcd_bh_exit, hcd);
+ //SCHEDULE_WORK(hcd->bh);
+ mxc_hcd_bh_exit((void *)hcd);
+ break;
+ }
+}
+
+/*!
+ * mxc_hcd_en_func() - otg hcd enable output function
+ * @param otg
+ * @param flag
+ */
+void mxc_hcd_en_func(struct otg_instance *otg, u8 flag)
+{
+ struct hcd_instance *hcd = otg->hcd;
+ struct mxc_hcd *mxc_hcd = hcd ? hcd->privdata : NULL;
+ struct tcd_instance *tcd = otg->tcd;
+ int i;
+ u32 hwmode = fs_rl(OTG_CORE_HWMODE);
+ unsigned long flags;
+
+ /* puts OTG capable port into a state where host is enabled */
+
+ local_irq_save(flags);
+ //fs_andl(OTG_CORE_HWMODE, 0xfffffff0); // clear
+
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(HCD, "SET");
+ //if (hwmode & MODULE_CRECFG_HOST) {
+ // printk(KERN_INFO"%s: warning FUNC STILL SET\n", __FUNCTION__);
+ //}
+ //printk(KERN_INFO"%s: SET\n", __FUNCTION__);
+ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_HOST); // set to software hnp
+ hcd_instance->active = TRUE;
+ if (!tcd->id) {
+ TRACE_MSG1(HCD, "FRM_INTRVL: %8x current", fs_rl(OTG_CORE_FRM_INTVL));
+ //fs_wl(OTG_CORE_FRM_INTVL, 4000 | MODULE_RESET_FRAME);
+ TRACE_MSG1(HCD, "FRM_INTRVL: %8x reset", fs_rl(OTG_CORE_FRM_INTVL));
+
+ }
+
+ break;
+ case RESET:
+ TRACE_MSG0(HCD, "RESET");
+ hcd_instance->active = FALSE;
+ fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_HOST); // set to software hnp
+ break;
+ }
+
+ local_irq_restore(flags);
+ TRACE_MSG1(HCD, "HWMODE: %08x", fs_rl(OTG_CORE_HWMODE));
+}
+
+u32 otg_core_frm_intvl;
+
+/*!
+ * mxc_hcd_rh_func() - otg hcd root hub output function
+ * @param otg
+ * @param flag
+ */
+void mxc_hcd_rh_func(struct otg_instance *otg, u8 flag)
+{
+ struct hcd_instance *hcd = otg->hcd;
+ struct tcd_instance *tcd = otg->tcd;
+ struct mxc_hcd *mxc_hcd = hcd ? hcd->privdata : NULL;
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ int i;
+ u32 mask;
+ unsigned long flags;
+
+ RETURN_UNLESS (hcd && mxc_hcd);
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+ switch (flag) {
+ case SET:
+ //printk(KERN_INFO"%s: SET\n", __FUNCTION__);
+
+ local_irq_save(flags);
+
+ /* reset the host core
+ */
+ fs_wl_clr(HCD, OTG_CORE_RST_CTRL, MODULE_RSTRH | MODULE_RSTHSIE | MODULE_RSTHC);
+ while (fs_rl(OTG_CORE_RST_CTRL));
+
+ mxc_host_clock_on();
+
+
+ for (i = 0; i < NUM_DATA_BUFFS; i++)
+ (void) rel_data_buff(mxc_hcd, ((fs_data_buff *)OTG_DATA_BASE)+i);
+#if 0
+ for (i = 0; i < NUM_ETDS; i++)
+ rel_etd_irq(mxc_hcd,i);
+#endif
+ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCRESET | HOST_CONTROL_RMTWUEN |
+ HOST_CONTROL_HCUSBSTE_RESET | HOST_CONTROL_CTLBLKSR_11);
+
+
+ TRACE_MSG0(HCD, "HW HCD_ENABLE_SET");
+ fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | MODULE_CMPEN |
+ MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ));
+
+ fs_rl(OTG_CORE_HNP_CSTAT);
+
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_MASTER | MODULE_CMPEN | MODULE_BGEN | MODULE_ABBUSREQ);
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_ARMTHNPE | MODULE_BHNPEN); // XXX
+
+ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL);
+
+ //hcd_hw_enable_interrupts(bus_hcpriv);
+
+ mxc_hcd->int_mask = (HOST_PSCINT_EN | HOST_FMOFINT_EN | HOST_HERRINT_EN |
+ HOST_RESDETINT_EN | /* HOST_SOFINT_EN | */ HOST_DONEINT_EN | HOST_SORINT_EN);
+
+ TRACE_MSG1(HCD, "HW HCD_ENABLE_SET: mask: %02x", mxc_hcd->int_mask);
+
+ #if 1
+ // R1: sec 23.11.15 pg 23-54
+ fs_rl(OTG_CORE_HNP_CSTAT);
+ fs_rl(OTG_HOST_CONTROL);
+ fs_rl(OTG_HOST_ROOTHUB_STATUS);
+ fs_rl(OTG_HOST_PORT_STATUS_1);
+ fs_rl(OTG_HOST_PORT_STATUS_2);
+ fs_rl(OTG_HOST_PORT_STATUS_3);
+
+ fs_wl(OTG_HOST_SINT_STEN, mxc_hcd->int_mask);
+ fs_rl(OTG_HOST_SINT_STEN);
+
+ mxc_hcd->otg_port_enabled = TRUE;
+ //printk(KERN_INFO"%s: is_b_host FALSE\n", __FUNCTION__);
+
+ // XXX need to get ID_GND
+
+ mxc_hcd->hcd.self.is_b_host = !tcd->id;
+
+ if (!tcd->id)
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_SWAUTORST); // XXX
+
+ for (i = 1; i <= mxc_hcd->bNbrPorts; i++) {
+ mxc_hcd_hw_rh_port_feature(mxc_hcd, PORT_POWER, i, TRUE);
+ //mxc_hcd->hub_port_change_status |= (1 << i);
+ }
+
+ #endif
+ local_irq_restore(flags);
+
+ break;
+
+ case RESET:
+
+ mxc_hcd->otg_port_enabled = FALSE; // XXX check this
+ mxc_hcd->hcd.self.is_b_host = TRUE;
+ //printk(KERN_INFO"%s: is_b_host TRUE\n", __FUNCTION__);
+
+ #if 0
+ local_irq_save(flags);
+
+ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "HW HCD_ENABLE_RESET");
+
+ //hcd_hw_disable_interrupts(bus_hcpriv);
+ // R1: sec 23.11.15 pg 23-54
+ fs_wl(OTG_HOST_SINT_STAT, 0);
+
+ fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_OPERATIONAL);
+
+ // Shut down hardware if not already shut down....
+ //hcd_hw_disable_interrupts(bus_hcpriv);
+
+ mxc_host_clock_off();
+ local_irq_restore(flags);
+ #endif
+ break;
+ }
+}
+
+/*!
+ * mxc_loc_sof_func() - otg loc sof output function
+ * @param otg
+ * @param flag
+ */
+void mxc_loc_sof_func(struct otg_instance *otg, u8 flag)
+{
+ struct hcd_instance *hcd = otg->hcd;
+ struct mxc_hcd *mxc_hcd = hcd ? hcd->privdata : NULL;
+ //struct tcd_instance *tcd = otg->tcd;
+ int i;
+ u32 hwmode = fs_rl(OTG_CORE_HWMODE);
+ unsigned long flags;
+
+ RETURN_UNLESS (hcd && mxc_hcd);
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+ switch (flag) {
+ case SET:
+ //printk(KERN_INFO"%s: SET\n", __FUNCTION__);
+
+ local_irq_save(flags);
+
+ #if 0
+ /* reset the host core
+ */
+ fs_wl_clr(HCD, OTG_CORE_RST_CTRL, MODULE_RSTRH | MODULE_RSTHSIE | MODULE_RSTHC);
+ while (fs_rl(OTG_CORE_RST_CTRL));
+
+ mxc_host_clock_on();
+
+ for (i = 0; i < NUM_DATA_BUFFS; i++)
+ (void) rel_data_buff(mxc_hcd, ((fs_data_buff *)OTG_DATA_BASE)+i);
+
+ for (i = 0; i < NUM_ETDS; i++)
+ rel_etd_irq(mxc_hcd,i);
+
+ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCRESET | HOST_CONTROL_RMTWUEN |
+ HOST_CONTROL_HCUSBSTE_RESET | HOST_CONTROL_CTLBLKSR_11);
+
+
+ TRACE_MSG0(HCD, "HW HCD_ENABLE_SET");
+ fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE | MODULE_CMPEN |
+ MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ));
+ fs_rl(OTG_CORE_HNP_CSTAT);
+
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_MASTER | MODULE_CMPEN | MODULE_BGEN | MODULE_ABBUSREQ);
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_ARMTHNPE | MODULE_BHNPEN); // XXX
+
+ fs_wl(OTG_HOST_CONTROL, HOST_CONTROL_HCUSBSTE_OPERATIONAL);
+
+ //hcd_hw_enable_interrupts(bus_hcpriv);
+
+ mxc_hcd->int_mask = (HOST_PSCINT_EN | HOST_FMOFINT_EN | HOST_HERRINT_EN |
+ HOST_RESDETINT_EN | /* HOST_SOFINT_EN | */ HOST_DONEINT_EN | HOST_SORINT_EN);
+
+ TRACE_MSG1(HCD, "HW HCD_ENABLE_SET: mask: %02x", mxc_hcd->int_mask);
+
+
+ // R1: sec 23.11.15 pg 23-54
+ fs_rl(OTG_CORE_HNP_CSTAT);
+ fs_rl(OTG_HOST_CONTROL);
+ fs_rl(OTG_HOST_ROOTHUB_STATUS);
+ fs_rl(OTG_HOST_PORT_STATUS_1);
+ fs_rl(OTG_HOST_PORT_STATUS_2);
+ fs_rl(OTG_HOST_PORT_STATUS_3);
+
+ fs_wl(OTG_HOST_SINT_STEN, mxc_hcd->int_mask);
+ fs_rl(OTG_HOST_SINT_STEN);
+
+ mxc_hcd->otg_port_enabled = TRUE;
+
+ for (i = 1; i <= mxc_hcd->bNbrPorts; i++) {
+ mxc_hcd_hw_rh_port_feature(mxc_hcd, PORT_POWER, i, TRUE);
+ //mxc_hcd->hub_port_change_status |= (1 << i);
+ }
+
+ #endif
+ local_irq_restore(flags);
+
+ break;
+
+ case RESET:
+ local_irq_save(flags);
+
+ //printk(KERN_INFO"%s: RESET\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "HW HCD_ENABLE_RESET");
+
+ //hcd_hw_disable_interrupts(bus_hcpriv);
+ // R1: sec 23.11.15 pg 23-54
+ fs_wl(OTG_HOST_SINT_STAT, 0);
+
+ fs_andl(OTG_HOST_CONTROL, ~HOST_CONTROL_HCUSBSTE_OPERATIONAL);
+
+ // Shut down hardware if not already shut down....
+ //hcd_hw_disable_interrupts(bus_hcpriv);
+
+ mxc_host_clock_off();
+ local_irq_restore(flags);
+ break;
+ }
+
+}
+/*! mxc_hcd_loc_suspend_func -
+ * @param otg
+ * @param on
+ */
+
+void mxc_hcd_loc_suspend_func(struct otg_instance *otg, u8 on)
+{
+ struct hcd_instance *hcd = otg->hcd;
+ struct mxc_hcd *mxc_hcd = hcd ? hcd->privdata : NULL;
+ int i;
+ unsigned long flags;
+
+ RETURN_UNLESS (hcd && mxc_hcd);
+
+ switch (on) {
+ case SET:
+ TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND SET");
+ break;
+
+ case RESET:
+ TRACE_MSG0(HCD, "OUTPUT: RH LOC_SUSPEND RESET");
+ break;
+ }
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * mxc_pcd_framenum() - get current framenum
+ * @param otg - otg instance
+ */
+static u16
+mxc_hcd_framenum (struct otg_instance *otg)
+{
+ //printk(KERN_INFO"%s: framenum: %04x\n", __FUNCTION__, fs_rl(OTG_HOST_FRM_NUM) );
+ return fs_rl(OTG_HOST_FRM_NUM);
+}
+
+
+
+#if !defined(OTG_C99)
+#if defined(LINUX26)
+int mxc_hcd_mod_init_l26(struct otg_instance *otg);
+void mxc_hcd_mod_exit_l26(struct otg_instance *otg);
+struct hcd_ops hcd_ops = { mxc_hcd_mod_init_l26, mxc_hcd_mod_exit_l26, };
+#else /* defined(LINUX26) */
+int mxc_hcd_mod_init_l24(struct otg_instance *otg);
+void mxc_hcd_mod_exit_l24(struct otg_instance *otg);
+struct hcd_ops hcd_ops = { mxc_hcd_mod_init_l24, mxc_hcd_mod_exit_l24, };
+#endif /* defined(LINUX26) */
+
+/*!
+ * fs_hcd_global_init() - initialize global vars for non C99 systems
+ */
+void fs_hcd_global_init(void)
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+ ZERO(hcd_ops);
+ hcd_ops.name = "MX21 HCD";
+ hcd_ops.max_ports = 1;
+ hcd_ops.capabilities = 0;
+ // module
+ hcd_ops.mod_init = mxc_hcd_mod_init; // called for module init
+#ifdef MODULE
+ hcd_ops.mod_exit = mxc_hcd_mod_exit; // called for module exit
+#endif
+ // otg state machine
+ hcd_ops.hcd_init_func = hcd_init_func; // initialize when otg enabled
+ hcd_ops.hcd_en_func = mxc_hcd_en_func; // setup hardware as host
+ hcd_ops.hcd_rh_func = mxc_hcd_rh_func; // start root hub
+ hcd_ops.loc_suspend_func = mxc_hcd_loc_suspend_func; // enable port on hub
+ hcd_ops.loc_sof_func = mxc_loc_sof_func; // enable port on hub
+ hcd_ops.framenum = mxc_hcd_framenum;
+};
+
+/* ********************************************************************************************* */
+#else /* !defined(OTG_C99) */
+int mxc_hcd_mod_init_l26(struct otg_instance *otg);
+void mxc_hcd_mod_exit_l26(struct otg_instance *otg);
+struct hcd_ops hcd_ops = {
+ .mod_init = mxc_hcd_mod_init_l26, // called for module init
+#ifdef MODULE
+ .mod_exit = mxc_hcd_mod_exit_l26, // called for module exit
+#endif
+
+ .name = "MX21 HCD",
+ .max_ports = 1,
+ .capabilities = 0,
+ // module
+ .hcd_init_func = hcd_init_func, // initialize when otg enabled
+ .hcd_en_func = mxc_hcd_en_func, // setup hardware as host
+ .hcd_rh_func = mxc_hcd_rh_func, // start root hub
+ .loc_suspend_func = mxc_hcd_loc_suspend_func, // enable port on hub
+ .loc_sof_func = mxc_loc_sof_func, // enable port on hub
+ .framenum = mxc_hcd_framenum,
+};
+#endif /* !defined(OTG_C99) */
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! Linux 2.6 USB Device Support
+ * New style linux 2.6.10 USB Core support
+ */
+
+/*
+ * From drivers/usb/host/hcd.c:
+ * disables the endpoint: cancels any pending urbs, then synchronizes with
+ * the hcd to make sure all endpoint state is gone from hardware. use for
+ * set_configuration, set_interface, driver removal, physical disconnect.
+ */
+/*! mxc_hcd_endpoint_disable -
+ * @param hcd - usb_hcd instance
+ * @param ep - endpoint
+ */
+void mxc_hcd_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ struct mxc_req *mxc_req;
+ unsigned long flags;
+ int is_out = (ep->desc.bEndpointAddress & USB_DIR_IN) ? 0 : 1;
+ unsigned int pipe = (unsigned int) ep->hcpriv;
+ int address = usb_pipedevice(pipe) ?
+ (usb_pipedevice(pipe) % MXC_MAX_USB_ADDRESS + 1) : 0;
+ TRACE_MSG0(HCD, "--");
+ local_irq_save(flags);
+
+ if ((mxc_req = mxc_hcd->ep[address][EPNUM(ep->desc.bEndpointAddress, is_out)])) {
+ //printk(KERN_INFO"%s: disabled\n", __FUNCTION__);
+ rel_etd_irq(mxc_hcd, mxc_req->etdn);
+ mxc_hcd->ep[address][EPNUM(ep->desc.bEndpointAddress, is_out)] = NULL;
+
+ // XXX giveback?
+ mxc_hcd_giveback_req_irq(mxc_hcd, mxc_req, 0); // check if 0 is correct status to return
+ }
+
+ local_irq_restore(flags);
+}
+
+int mxc_rh_frame(struct usb_hcd *hcd)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ TRACE_MSG0(HCD, "--");
+ return fs_rl(OTG_HOST_FRM_NUM);
+}
+
+/*! mxc_rh_descriptor -get root hub descriptor
+ * @param mxc_hcd - mxc_hcd instance
+ * @param desc - root hub descriptor pointer
+ */
+static void
+mxc_rh_descriptor ( struct mxc_hcd *mxc_hcd, struct usbp_hub_descriptor *desc)
+{
+ u16 temp = 0;
+
+ desc->bDescLength = 9;
+ desc->bDescriptorType = 0x29;
+
+ desc->bNbrPorts = mxc_hcd->bNbrPorts;
+ desc->wHubCharacteristics = mxc_hcd->wHubCharacteristics;
+ desc->bPwrOn2PwrGood = mxc_hcd->bPwrOn2PwrGood;
+ desc->bHubContrCurrent = 0;
+ desc->DeviceRemovable = mxc_hcd->DeviceRemovable;
+ desc->PortPwrCtrlMask = mxc_hcd->PortPwrCtrlMask;
+}
+
+
+/*! mxc_rh_status_data() - provide hub status data to usb core root hub
+ *
+ * This function is used by the linux 2.6 virtual root hub to poll for
+ * changes.
+ * @param hcd - usb_hcd instance
+ * @param buf -
+ *
+ */
+int mxc_rh_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+
+ RETURN_ZERO_UNLESS(mxc_hcd->otg_port_enabled);
+ RETURN_ZERO_UNLESS(mxc_hcd->virt_hub_port_change_status);
+
+ *buf = mxc_hcd->virt_hub_port_change_status;
+ mxc_hcd->virt_hub_port_change_status = 0;
+
+ TRACE_MSG1(HCD, "changed buf: %02x", *buf);
+ return 1;
+}
+/*! mxc_rh_control - called to process control endpoint request
+ *
+ * @param hcd -
+ * @param typeReq
+ * @param wValue
+ * @param wIndex
+ * @param buf
+ * @param wLength
+ * @return
+ */
+int mxc_rh_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ unsigned long flags;
+ u32 status;
+
+ TRACE_MSG4(HCD, "typeReq: %04x wValue: %04x wIndex: %04x wLength: %04x", typeReq, wValue, wIndex, wLength);
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ case SetHubFeature:
+ TRACE_MSG0(HCD, "ClearHubFeature/SetHubFeature");
+ //mxc_rh_hub_feature(mxc_hcd, wValue, wIndex, typeReq == SetHubFeature);
+ return 0;
+
+ case SetPortFeature: {
+ u32 virt_port_status;
+ TRACE_MSG0(HCD, "SetPortFeature");
+ RETURN_EPIPE_IF (wIndex > mxc_hcd->bNbrPorts || wLength);
+ virt_port_status = mxc_hcd->virt_port_status[wIndex - 1];
+ /* Feature selector is wValue, wIndex is 1-origin
+ */
+ // SET feature
+ switch (wValue) {
+ case PORT_RESET:
+ mxc_hcd->virt_port_status[wIndex - 1] |= (1 << C_PORT_RESET | (1 << wValue));
+ mxc_hcd->virt_hub_port_change_status |= (1 << wIndex);
+ break;
+ case PORT_SUSPEND:
+ case PORT_POWER:
+ mxc_hcd->virt_port_status[wIndex - 1] |= (1 << wValue);
+ break;
+ }
+ TRACE_MSG3(HCD, "[%d]: %08x -> %08x", wIndex, virt_port_status, mxc_hcd->virt_port_status[wIndex -1]);
+ //printk(KERN_INFO"%s: SetPortFeature[%d]: wValue: %d %08x -> %08x %s\n",
+ // __FUNCTION__, wIndex, wValue, virt_port_status, mxc_hcd->virt_port_status[wIndex -1],
+ // port_feature_name[wValue]);
+ return 0;
+ }
+ case ClearPortFeature: {
+ u32 virt_port_status;
+ TRACE_MSG0(HCD, "ClearPortFeature");
+ RETURN_EPIPE_IF ((wIndex > mxc_hcd->bNbrPorts) || wLength);
+ virt_port_status = mxc_hcd->virt_port_status[wIndex - 1];
+ /* Feature selector is wValue, wIndex is 1-origin
+ */
+ // CLEAR feature (valid features from USB2.0 11.24.2.2 pg 423).
+ switch (wValue) {
+ case PORT_ENABLE: // Disable port.
+ case PORT_SUSPEND: // Cause a Host initiated resume, or no-op if already active.
+ case PORT_POWER: // Put port in powered-off state.
+ case C_PORT_CONNECTION: // clear the PORT_CONNECTION change bit
+ case C_PORT_RESET: // clear the PORT_RESET change bit
+ case C_PORT_ENABLE: // clear the PORT_ENABLE change bit
+ case C_PORT_SUSPEND: // clear the PORT_SUSPEND change bit
+ case C_PORT_OVER_CURRENT: // clear the PORT_OVERCURRENT change bit
+ mxc_hcd->virt_port_status[wIndex - 1] &= ~(1 << wValue);
+ break;
+ }
+ TRACE_MSG3(HCD, "[%d]: %08x -> %08x", wIndex, virt_port_status, mxc_hcd->virt_port_status[wIndex -1]);
+ //printk(KERN_INFO"%s: ClearPortFeature[%d]: wValue: %d %08x -> %08x %s\n",
+ // __FUNCTION__, wIndex, wValue, virt_port_status, mxc_hcd->virt_port_status[wIndex -1],
+ // port_feature_name[wValue]);
+ return 0;
+ }
+
+ case GetHubDescriptor:
+ //printk(KERN_INFO"%s: GetHubDescriptor\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "GetHubDescriptor");
+ mxc_rh_descriptor(mxc_hcd, (struct usbp_hub_descriptor *) buf);
+ return 0;
+
+ case GetHubStatus:
+ //printk(KERN_INFO"%s: GetHubStatus\n", __FUNCTION__);
+ //*(__le32 *) buf = cpu_to_le32(0); // XXX this seems to be de rigeur for 2.6 root hubs...
+ *(__le32 *) buf = cpu_to_le32(fs_rl(OTG_HOST_ROOTHUB_STATUS) & 0xffff);
+ TRACE_MSG1(HCD, "GetHubStatus: %04x", *(__le32 *)buf);
+ return 0;
+
+ case GetPortStatus:
+ //printk(KERN_INFO"%s: GetPortStatus\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "GetPortStatus");
+ RETURN_EPIPE_IF ((wIndex > mxc_hcd->bNbrPorts));
+ /* use saved port change status and reset it
+ */
+ status = mxc_hcd->virt_port_status[wIndex - 1]; // | fs_rl(fs_host_port_stat(wIndex))
+ *(__le32 *) buf = cpu_to_le32( status);
+
+ TRACE_MSG3(HCD, "GetPortStatus: port: %d port_change_status: %08x buf: %08x",
+ wIndex, status, *(__le32 *) buf);
+
+ //printk(KERN_INFO"%s: GetPortStatus port: %d status: %08x\n",
+ // __FUNCTION__, wIndex, status);
+
+ /* Mini-state machine to progress to PORT_CONNECT in virtual root hub port */
+ if (status & (1 << PORT_RESET)) {
+ mxc_hcd->virt_port_status[wIndex - 1] &= ~(1 << PORT_RESET);
+ mxc_hcd->virt_port_status[wIndex - 1] |=
+ (1 << C_PORT_RESET) |
+ (1 << C_PORT_ENABLE) |
+ (1 << PORT_ENABLE)
+ ;
+ mxc_hcd->virt_hub_port_change_status |= (1 << wIndex);
+ //printk(KERN_INFO"%s: GetPortStatus port: %d status: %08x CLEARED PORT_RESET\n",
+ // __FUNCTION__, wIndex, mxc_hcd->virt_port_status[wIndex - 1]);
+ }
+ return 0;
+
+ default:
+ //printk(KERN_INFO"%s: Default\n", __FUNCTION__);
+ /* "protocol stall" on error */
+ return -EPIPE;
+ }
+}
+
+/*! mxc_rh_suspend - called to suspend root hub
+ * @param hcd - usb_hcd instance
+ */
+int mxc_rh_suspend(struct usb_hcd *hcd)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ TRACE_MSG0(HCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return 0;
+}
+/*! mxc_rh_resume - called to resum root hub
+ * @param hcd - usb_hcd instance
+ * @return 0
+ */
+int mxc_rh_resume(struct usb_hcd *hcd)
+{
+ struct mxc_hcd *mxc_hcd = hcd_to_mxc(hcd);
+ TRACE_MSG0(HCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return 0;
+}
+
+/*! mxc_hcd_stop - called to stop hcd
+ * @param hcd - usb_hcd instance
+ */
+static void
+mxc_hcd_stop(struct usb_hcd *hcd)
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG0(HCD, "--");
+ del_timer_sync(&hcd->rh_timer);
+}
+
+static int
+mxc_hcd_start(struct usb_hcd *hcd)
+{
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+/*! struct hcd_driver mxc_hc_driver */
+static const struct hc_driver mxc_hc_driver = {
+ .description = "mxc-hcd",
+ .product_desc = "Freescale On-Chip USB Host Controller",
+ .hcd_priv_size = sizeof(struct mxc_hcd) - sizeof(struct usb_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .flags = HCD_USB11,
+
+ .start = mxc_hcd_start,
+ .stop = mxc_hcd_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = mxc_hcd_urb_enqueue,
+ .urb_dequeue = mxc_hcd_urb_dequeue,
+ .endpoint_disable = mxc_hcd_endpoint_disable,
+
+ /*
+ * periodic schedule support
+ */
+ .get_frame_number = mxc_rh_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = mxc_rh_status_data,
+ .hub_control = mxc_rh_control,
+ .bus_suspend = mxc_rh_suspend,
+ .bus_resume = mxc_rh_resume,
+};
+
+/* ********************************************************************************************* */
+/*! mxc_hcd_remove - called to remove hcd
+ * @param dev
+ */
+static int
+mxc_hcd_remove(struct platform_device *dev)
+{
+ struct mxc_hcd *hcd = platform_get_drvdata(dev);
+
+ usb_remove_hcd(&hcd->hcd);
+
+ usb_put_hcd(&hcd->hcd);
+
+ return 0;
+}
+
+static void mxc_dev_release(struct platform_device *device)
+{
+ TRACE_MSG0(HCD, "--");
+ //usb_hcd_release((struct usb_bus *) dev);
+}
+
+/*! mxc_hcd_probe - called to initialize hcd
+ * @param dev - hcd device
+ */
+static int mxc_hcd_probe(struct platform_device *dev)
+{
+ struct platform_device *platform_device = NULL;
+ struct mxc_hcd *hcd;
+
+ hcd = (struct mxc_hcd *) usb_create_hcd(&mxc_hc_driver, &dev->dev, dev->dev.bus_id);
+ if (!hcd) {
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&hcd->unused);
+ INIT_LIST_HEAD(&hcd->inactive);
+
+ platform_set_drvdata(dev, hcd);
+
+ //usb_bus_init(&hcd->hcd.self);
+ //hcd->hcd.self.op = &usb_hcd_operations;
+ hcd->hcd.self.hcpriv = hcd;
+ hcd->hcd.self.is_b_host = TRUE;
+ hcd->hcd.self.otg_port = 1;
+ hcd->root_hub_desc_a = fs_rl(OTG_HOST_ROOTHUB_DESCA);
+ hcd->root_hub_desc_b = fs_rl(OTG_HOST_ROOTHUB_DESCB);
+ hcd->bNbrPorts = hcd->root_hub_desc_a & 0xff;
+ hcd->wHubCharacteristics = (hcd->root_hub_desc_a >> 8) & 0xff;
+ hcd->bPwrOn2PwrGood = (hcd->root_hub_desc_a >> 24) & 0xff;
+ hcd->DeviceRemovable = hcd->root_hub_desc_b & 0xff;
+ hcd->PortPwrCtrlMask = (hcd->root_hub_desc_b >> 16) & 0xff;
+ hcd->allocated_count = hcd->active_count = 0;
+
+ TRACE_MSG2(HCD, "root_hub descA: %08x descB: %08x", hcd->root_hub_desc_a, hcd->root_hub_desc_b);
+
+ TRACE_MSG5(HCD, "bNbrPorts: %02x wHubCharacteristics: %02x bPwrOn2PwrGood: %02x "
+ "DeviceRemovalbe: %02x PortPwrCtrlMask: %02x",
+ hcd->bNbrPorts,
+ hcd->wHubCharacteristics,
+ hcd->bPwrOn2PwrGood,
+ hcd->DeviceRemovable,
+ hcd->PortPwrCtrlMask
+ );
+
+ //INIT_LIST_HEAD(&hcd->hcd.dev_list);
+
+ //hcd->hcd.self.release = &mxc_hcd_release;
+
+ hcd->hcd.driver = &mxc_hc_driver;
+ hcd->hcd.irq = -1;
+ //hcd->hcd.state = USB_STATE_HALT;
+
+ spin_lock_init(&hcd->lock);
+
+ THROW_IF(usb_add_hcd(&hcd->hcd, 0, IRQF_SHARED), error);;
+
+ hcd_instance->privdata = &hcd->hcd;
+
+ THROW_IF(mxc_host_gpio(), error);
+
+ return 0;
+
+ CATCH(error) {
+ if (hcd) {
+ if (hcd) mxc_hcd_stop(&hcd->hcd);
+ usb_put_hcd(&hcd->hcd);
+ }
+ return -ENODEV;
+ }
+}
+
+/*! mxc_hcd_driver - define a platform_driver structure for this driver
+ */
+static struct platform_driver mxc_hcd_driver = {
+ .driver = {
+ .name = "mxc-hcd",
+ },
+
+ .probe = mxc_hcd_probe,
+ .remove = mxc_hcd_remove,
+};
+
+static void mxc_hcd_release(struct usb_bus *bus)
+{
+ TRACE_MSG0(HCD, "--");
+ //usb_hcd_release((struct usb_bus *) dev);
+}
+
+/*! mxc_hcd_platform - define a platform_device structure for this driver
+ */
+static struct platform_device mxc_hcd_platform = {
+ .name = "mxc-hcd",
+ .id = 1,
+};
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+extern char *etd_urb_state_name[];
+/*! mxc_hcd_show - called to display hcd information
+ * @param s
+ * @param unused
+ */
+static int mxc_hcd_show(struct seq_file *s, void *unused)
+{
+ struct mxc_hcd *mxc_hcd = hcd_instance->privdata; // XXX this should come from dev_id
+ struct mxc_req *mxc_req;
+ struct mxc_req *mxc_req_save;
+
+ int etdn;
+ unsigned long flags;
+
+
+ seq_printf(s, "MXC HCD\n\n");
+
+ for (etdn = 0; etdn < NUM_ETDS; etdn++) {
+
+ local_irq_save(flags);
+
+ if ((mxc_req = mxc_hcd->active[etdn])) {
+ seq_printf(s, "[%02x] Active urb: %08x epnum: %02x state: %s\n", etdn,
+ mxc_req->urb,
+ mxc_req->epnum,
+ etd_urb_state_name[mxc_req->etd_urb_state]
+ );
+ }
+
+ local_irq_restore(flags);
+
+ }
+ seq_printf(s, "\n");
+
+ local_irq_save(flags);
+ etdn = 0;
+ list_for_each_entry(mxc_req, &mxc_hcd->inactive, queue) {
+ seq_printf(s, "[%02x] Inactive urb: %08x epnum: %02x state: %s\n", etdn++,
+ mxc_req->urb,
+ mxc_req->epnum,
+ etd_urb_state_name[mxc_req->etd_urb_state]
+ );
+ }
+ local_irq_restore(flags);
+
+ seq_printf(s, "\n");
+ seq_printf(s, "Requests: Allocated: %d Active: %d Inactive: %d Total: %d\n",
+ mxc_hcd->allocated_count, mxc_hcd->active_count, etdn, mxc_hcd->request_count);
+
+ seq_printf(s, "\n");
+ return 0;
+}
+
+/*! mxc_hcd_open - open a hcd file
+ * @param inode
+ * @param file
+ */
+
+static int mxc_hcd_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mxc_hcd_show, PDE(inode)->data);
+}
+/*! struct file_operatons mxc_hcd_proc_ops */
+static struct file_operations mxc_hcd_proc_ops = {
+ .open = mxc_hcd_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const char proc_filename[] = "mxchcd";
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*! Module init/exit
+ *
+ * Register this a linux device driver and platform. This provides the necessary framework
+ * expected by the Linux 2.6 USB Core.
+ *
+ */
+/*! mxc_hcd_mod_init_l26 -called to register hcd driver and platform
+ * @param otg - otg instance
+ */
+
+int mxc_hcd_mod_init_l26(struct otg_instance *otg)
+{
+ u32 hwmode;
+ int driver_registered = 0;
+ int platform_registered = 0;
+ struct proc_dir_entry *pde = NULL;
+
+ printk(KERN_INFO"%s: MXC-HCD.C (dev)\n", __FUNCTION__);
+
+ if (usb_disabled()) {
+ //printk(KERN_INFO"%s: YYYYY\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ THROW_IF((driver_registered = platform_driver_register(&mxc_hcd_driver)), error);
+
+ THROW_IF((platform_registered = platform_device_register(&mxc_hcd_platform)), error);
+
+ THROW_UNLESS((pde = create_proc_entry(proc_filename, 0, NULL)), error);
+ pde->proc_fops = &mxc_hcd_proc_ops;
+
+ CATCH(error) {
+ //printk(KERN_INFO"%s: ZZZZ\n", __FUNCTION__);
+ if (platform_registered) platform_device_unregister(&mxc_hcd_platform);
+ if (driver_registered) platform_driver_unregister(&mxc_hcd_driver);
+ if (pde) remove_proc_entry(proc_filename, NULL);
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+/*! mxc_hcd_mod_exit_l26 - called to release hcd module
+ * @param otg - otg instance
+ */
+void mxc_hcd_mod_exit_l26(struct otg_instance *otg)
+{
+ remove_proc_entry(proc_filename, NULL);
+
+ platform_driver_unregister(&mxc_hcd_driver);
+
+ platform_device_unregister(&mxc_hcd_platform);
+
+}
+#endif
diff --git a/drivers/otg/hardware/mxc-lnx.h b/drivers/otg/hardware/mxc-lnx.h
new file mode 100644
index 000000000000..f62a13b18e43
--- /dev/null
+++ b/drivers/otg/hardware/mxc-lnx.h
@@ -0,0 +1,546 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-lnx.h
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/mxc/mxc-lnx.h|20070726000034|21621
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/hardware/mxc-lnx.h
+ * @brief Hardware defines for Freescale USBOTG Hardware
+ *
+ * This supports the Freescale MXC implementation of the Trans Dimension
+ * USBOTG.
+ *
+ * This is used on:
+ *
+ * - i.MX21
+ * - SCM-A11
+ * - Argon+
+ * - Zeus
+ *
+ * @ingroup FSOTG
+ * @ingroup LINUXAPI
+ */
+
+#if defined(CONFIG_ARCH_MX2ADS)
+#include <linux/pci.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/gpt.h>
+#undef GPT_BASE_ADDR
+#include <asm/arch/mx2.h>
+//#include <otghw/mx2ads.h>
+//#include <otghw/mx2ads-hardware.h>
+#endif /* defined(CONFIG_ARCH_MX2ADS) */
+
+//#if defined(CONFIG_ARCH_SCMA11) || defined(CONFIG_ARCH_ARGONPLUS) || defined(CONFIG_ARCH_ARGONLV) || defined(CONFIG_ARCH_ZEUS)
+#if defined(CONFIG_ARCH_MXC91231) || defined(CONFIG_ARCH_MXC91331) || defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91131)
+#include <linux/pci.h>
+//#include <otghw/scma11.h>
+//#include <otghw/scma11-hardware.h>
+#endif /* defined(CONFIG_ARCH_SCMA11) */
+
+
+
+#if defined(CONFIG_ARCH_MX2ADS)
+
+/*!
+ * @name MX2ETDS
+ * R1 = i.MX21 Application Processor Reference Manual Rev 0.6 2003/11/01 TO 1.0
+ * (aka "IMX21RM_TO1.pdf")
+ * R2 = "USBOTG_L3_Specification_v.C1.0.pdf"
+ * @{
+ */
+#define NUM_ETDS 32
+#define DATA_BUFF_SIZE 64
+#define DATA_BUFFER_TOTAL 4096
+#define NUM_DATA_BUFFS (4096/DATA_BUFF_SIZE)
+#define LITTLE_ENDIAN 1
+
+
+#define UDC_NAME "MX21"
+#define UDC_MAX_ENDPOINTS 32
+//#define EP0_PACKETSIZE 8
+#define EP0_PACKETSIZE 64
+
+
+/* @} */
+
+/*!
+ * @name MX2Interrupts
+ * @{
+ */
+#define OTG_USBWKUP 53
+#define OTG_USBDMA 54
+#define OTG_USBHOST 55
+#define OTG_USBFUNC 56
+#define OTG_USBHNP 57
+#define OTG_USBCTRL 58
+/* @} */
+
+/*!
+ * @name I2CTransceiver
+ * C.f. 23.14.10 I2C OTG Transceiver Controller Registers
+ * These are the I2C controller and access registers.
+ *
+ * N.B. I2C_ERROR is not documented.
+ * @{
+ */
+
+#define I2C_BUSY (1 << 7)
+#define I2C_ERROR (1 << 2)
+#define I2C_HWSMODE (1 << 1)
+#define I2C_I2COE (1 << 0)
+
+#define I2C_SCLK_TO_SCL_DIVISION (OTG_I2C_BASE+0x1E)
+
+#define I2C_INTERRUPT_AND_CONTROL (OTG_I2C_BASE+0x1F)
+
+#define I2C_STATUS_MASK (0x7)
+
+#define I2C_NOACK_EN (1 << 6)
+#define I2C_RWREADY_EN (1 << 5)
+#define I2C_OTGXCVRINT_EN (1 << 4)
+
+#define I2C_NOACK (1 << 2)
+#define I2C_RWREADY (1 << 1)
+#define I2C_OTGXCVRINT (1 << 0)
+
+#define MX2_OTG_XCVR_DEVAD OTG_I2C_BASE+0x18
+#define MX2_SEQ_OP_REG OTG_I2C_BASE+0x19
+#define MX2_SEQ_RD_STARTAD OTG_I2C_BASE+0x1a
+#define MX2_I2C_OP_CTRL_REG OTG_I2C_BASE+0x1b
+#define MX2_SCLK_TO_SCL_HPER OTG_I2C_BASE+0x1e
+#define MX2_I2C_INTERRUPT_AND_CTRL OTG_I2C_BASE+0x1f
+
+/* @} */
+
+#endif /* defined(CONFIG_ARCH_MX2ADS) */
+
+
+//#if defined(CONFIG_ARCH_SCMA11) || defined(CONFIG_ARCH_ARGONPLUS) || defined(CONFIG_ARCH_ARGONLV) || defined(CONFIG_ARCH_ZEUS)
+#if defined(CONFIG_ARCH_MXC91231) || defined(CONFIG_ARCH_MXC91331) || defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91131)
+
+/*!
+ * @name MX2ETDS
+ * R1 = i.MX21 Application Processor Reference Manual Rev 0.6 2003/11/01 TO 1.0
+ * (aka "IMX21RM_TO1.pdf")
+ * R2 = "USBOTG_L3_Specification_v.C1.0.pdf"
+ * @{
+ */
+
+#define OTG_USBDMA 54
+
+#define NUM_ETDS 16
+#define DATA_BUFF_SIZE 64
+#define DATA_BUFFER_TOTAL 4096
+#define NUM_DATA_BUFFS (4096/DATA_BUFF_SIZE)
+#define LITTLE_ENDIAN 1
+
+
+#define UDC_NAME "MXC"
+#define UDC_MAX_ENDPOINTS 16
+#define EP0_PACKETSIZE 64
+
+
+/* @} */
+
+
+#if defined (CONFIG_ARCH_MXC91331) || defined(CONFIG_ARCH_MXC91321)
+#define OTG_BASE_ADDR 0x50020000
+#define INT_USB_WAKEUP 35
+#define INT_USB_SOF 36
+#define INT_PMU_EVTMON 37
+#define INT_USB_FUNC 38
+#define INT_USB_DMA 39
+#define INT_USB_CTRL 40
+#define _reg_GPT_GPTCR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x00)))
+#define _reg_GPT_GPTPR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x04)))
+#define _reg_GPT_GPTSR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x08)))
+#define _reg_GPT_GPTIR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x0C)))
+#define _reg_GPT_GPTOCR1 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x10)))
+#define _reg_GPT_GPTOCR2 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x14)))
+#define _reg_GPT_GPTOCR3 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x18)))
+#define _reg_GPT_GPTICR1 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x1C)))
+#define _reg_GPT_GPTICR2 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x20)))
+#define _reg_GPT_GPTCNT ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x24)))
+
+#elif defined (CONFIG_ARCH_MXC91231)
+#define OTG_BASE_ADDR 0x50024000
+
+#elif defined (CONFIG_ARCH_MXC91131)
+#define OTG_BASE_ADDR 0x50024000
+
+#define _reg_GPT_GPTCR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x00)))
+#define _reg_GPT_GPTPR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x04)))
+#define _reg_GPT_GPTSR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x08)))
+#define _reg_GPT_GPTIR ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x0C)))
+#define _reg_GPT_GPTOCR1 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x10)))
+#define _reg_GPT_GPTOCR2 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x14)))
+#define _reg_GPT_GPTOCR3 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x18)))
+#define _reg_GPT_GPTICR1 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x1C)))
+#define _reg_GPT_GPTICR2 ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x20)))
+#define _reg_GPT_GPTCNT ((volatile __u32 *)(IO_ADDRESS(GPT_BASE_ADDR + 0x24)))
+
+
+#endif /* defined (CONFIG_ARCH_XXXX) */
+
+#define OTG_CORE_BASE (OTG_BASE_ADDR+0x000) // base location for core
+#define OTG_FUNC_BASE (OTG_BASE_ADDR+0x040) // base location for function
+#define OTG_HOST_BASE (OTG_BASE_ADDR+0x080) // base location for host
+#define OTG_DMA_BASE (OTG_BASE_ADDR+0x800) // base location for dma
+#define OTG_ETD_BASE (OTG_BASE_ADDR+0x200) // base location for etd memory
+#define OTG_EP_BASE (OTG_BASE_ADDR+0x400) // base location for ep memory
+#define OTG_SYS_BASE (OTG_BASE_ADDR+0x600) // base location for system
+
+//#if defined (CONFIG_ARCH_SCMA11)
+#if defined (CONFIG_ARCH_MXC91231)
+#define OTG_DATA_BASE (OTG_BASE_ADDR+0x4000) // base location for data memory
+#else /* defined (CONFIG_ARCH_SCMA11) */
+#define OTG_DATA_BASE (OTG_BASE_ADDR+0x1000) // base location for data memory
+#endif /* defined (CONFIG_ARCH_SCMA11) */
+
+#define OTG_SYS_CTRL (OTG_SYS_BASE+0x000) // base location for system
+
+/* @} */
+
+#endif /* defined(CONFIG_ARCH_SCMA11) */
+
+
+/*!
+ * @name C.f. 23.11.11 Host Registers
+ */
+ /*! @{ */
+
+/* ep descriptor access
+ */
+static __inline__ u32 etd_word(int n, int word)
+{
+ u32 offset = n * 16;
+ offset += word * 4;
+ return OTG_ETD_BASE + offset;
+}
+
+/* ep descriptor access
+ */
+static __inline__ u32 ep_word(int n, int dir, int word)
+{
+ u32 offset = n * 2;
+ offset += dir ? 1 : 0;
+ offset *= 16;
+ offset += word * 4;
+ return OTG_EP_BASE + offset;
+}
+
+/* endpoint data buffer access
+ *
+ * This is a simplistic allocator, will do until we want to support ISO or host mode.
+ *
+ * This works because we are assuming a maximum of 16 allocate endpoints, and no
+ * overlapped endpoints (both in and out are allocated).
+ */
+
+#define VOLATILE
+static VOLATILE __inline__ u16 data_x_buf(int n, int dir)
+{
+ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0));
+}
+static VOLATILE __inline__ u16 data_y_buf(int n, int dir)
+{
+ return 0x40 * (n * 4 + 2 * (dir ? 1 : 0) + 1);
+}
+
+static VOLATILE __inline__ u8 * data_x_address(int n, int dir)
+{
+ return (VOLATILE u8 *) IO_ADDRESS(OTG_DATA_BASE + data_x_buf(n, dir));
+}
+static VOLATILE __inline__ u8 * data_y_address(int n, int dir)
+{
+ return (VOLATILE u8 *) IO_ADDRESS(OTG_DATA_BASE + data_y_buf(n, dir));
+}
+/*! @} */
+
+
+/* ********************************************************************************************** */
+
+#if 0
+/*!
+ * rel_data_buff() - release data buff
+ */
+static __inline__ fs_data_buff *rel_data_buff(fs_hcpriv *fs_hcpriv, fs_data_buff *db)
+{
+ // Release db to the pool of available data_buffs.
+ unsigned long flags;
+ u16 ndx;
+ if (NULL != db) {
+ local_irq_save(flags);
+ ndx = db - (fs_data_buff *)OTG_DATA_BASE;
+ fs_hcpriv->buff_list[ndx] = fs_hcpriv->free_buffs;
+ fs_hcpriv->free_buffs = ndx;
+#if 0
+ db->next = fs_hcpriv->free_buffs;
+ fs_hcpriv->free_buffs = db;
+#endif
+ local_irq_restore(flags);
+ }
+ return(NULL);
+}
+
+/*!
+ * data_buff_boff() - get data buffer offset given address
+ */
+static __inline__ u16 data_buff_boff(fs_data_buff *db)
+{
+ return((u16)(((void *) db) - ((void *) OTG_DATA_BASE)));
+}
+
+/*!
+ * data_buff_addr() - get data buffer address given offset
+ */
+static __inline__ fs_data_buff *data_buff_addr(u16 boff)
+{
+ // Return the address of the fs_data_buffer that is boff bytes from the start of data buffer memory.
+ return(boff + ((void *) OTG_DATA_BASE));
+}
+/*! @} */
+#endif
+
+/* ********************************************************************************************** */
+
+/*!
+ * @name FSUSBOTGIO
+ * @brief Freescale USBOTG I/O support.
+ */
+ /*! @{ */
+
+/*!
+ * fs_rb() - read a byte
+ * @param port
+ * @return byte read
+ */
+static u8 __inline__ fs_rb(u32 port)
+{
+ return *(volatile u8 *) (IO_ADDRESS(port));
+}
+
+/*!
+ * fs_rl() - read a long
+ * @param port
+ * @return word read
+ */
+static u32 __inline__ fs_rl(u32 port)
+{
+ return *(volatile u32 *) (IO_ADDRESS(port));
+}
+
+/*!
+ * fs_wb() - write a byte
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_wb(u32 port, u8 val)
+{
+ *(volatile u8 *)(IO_ADDRESS(port)) = val;
+}
+
+/*!
+ * fs_orb() - or a byte
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_orb(u32 port, u8 val)
+{
+ u8 set = fs_rb(port) | val;
+ *(volatile u8 *)(IO_ADDRESS(port)) = set;
+}
+
+/*!
+ * fs_andb() - and a byte
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_andb(u32 port, u8 val)
+{
+ u8 set = fs_rb(port) & val;
+ *(volatile u8 *)(IO_ADDRESS(port)) = set;
+}
+
+/*!
+ * fs_wl() - write a long
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_wl(u32 port, u32 val)
+{
+ u32 set;
+ *(volatile u32 *)(IO_ADDRESS(port)) = val;
+ set = fs_rl(port);
+}
+
+/*!
+ * fs_orl() - or a long
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_orl(u32 port, u32 val)
+{
+ u32 set = fs_rl(port);
+ *(volatile u32 *)(IO_ADDRESS(port)) = (set | val);
+}
+
+/*!
+ * fs_andl() - and a long
+ * @param port
+ * @param val
+ */
+static void __inline__ fs_andl(u32 port, u32 val)
+{
+ u32 set = fs_rl(port);
+ *(volatile u32 *)(IO_ADDRESS(port)) = (set & val);
+}
+
+/*!
+ * fs_wl_set() - set a word and verify
+ * @param tag
+ * @param port
+ * @param val
+ */
+static void inline fs_wl_set(otg_tag_t tag, u32 port, u32 val)
+{
+ u32 set;
+ *(volatile u32 *)(IO_ADDRESS(port)) = val;
+#if 0
+ set = fs_rl(port);
+ if ((set & val) != val) {
+ TRACE_MSG1(tag, "SET FAILED: %08x", set);
+ }
+#endif
+}
+
+#if 1
+/*!
+ * fs_wl_clr() - clr a word and verify
+ * @param tag
+ * @param port
+ * @param clr
+ */
+static void inline fs_wl_clr(otg_tag_t tag, u32 port, u32 clr)
+{
+ u32 set;
+ *(volatile u32 *)(IO_ADDRESS(port)) = clr;
+ set = fs_rl(port);
+ if (set & clr) {
+ TRACE_MSG1(tag, "CLEAR FAILED 1: %08x", set);
+ *(volatile u32 *)(IO_ADDRESS(port)) = clr;
+ set = fs_rl(port);
+#if 1
+ if (set & clr)
+ TRACE_MSG1(tag, "CLEAR FAILED 2: %08x", set);
+#endif
+ }
+}
+#endif
+
+/*!
+ * fs_memcpy32() - emulate memcpy using long copy
+ * @param dp destination pointer
+ * @param sp source pointer
+ * @param words number of 32bit words to copy
+ *
+ */
+static void inline fs_memcpy32(u32 *dp, u32 *sp, volatile int words)
+{
+ while (words--) *dp++ = *sp++;
+}
+/*!
+ * fs_memcpy() - emulate memcpy using byte copy
+ * @param dp destination pointer
+ * @param sp source pointer
+ * @param bytes number of 8bit bytes to copy
+ */
+static void inline fs_memcpy(u8 *dp, u8 *sp, volatile int bytes)
+{
+ while (bytes--) *dp++ = *sp++;
+}
+/*!
+ * fs_clear_words() - clear memory
+ * @param addr address to clear from
+ * @param words number of 32bit words to clear.
+ */
+static void inline fs_clear_words(volatile u32 *addr, int words)
+{
+ while (words--) *addr++ = 0;
+}
+
+
+
+/*! @} */
+
+//#if defined(CONFIG_ARCH_SCMA11) || defined(CONFIG_ARCH_ARGONPLUS) || defined(CONFIG_ARCH_ARGONLV) || defined(CONFIG_ARCH_ZEUS)
+#if defined(CONFIG_ARCH_MXC91231) || defined(CONFIG_ARCH_MXC91331) || defined(CONFIG_ARCH_MXC91321) || defined(CONFIG_ARCH_MXC91131)
+
+#define _reg_CRM_AP_ASCSR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x00)))
+#define _reg_CRM_AP_ACDR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x04)))
+#define _reg_CRM_AP_ACDER1 ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x08)))
+#define _reg_CRM_AP_ACDER2 ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x0c)))
+
+#define _reg_CRM_AP_ACGCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x10)))
+#define _reg_CRM_AP_ACCGCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x14)))
+#define _reg_CRM_AP_AMLPMRA ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x18)))
+#define _reg_CRM_AP_AMLPMRB ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x1c)))
+
+#define _reg_CRM_AP_AMLPMRC ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x20)))
+#define _reg_CRM_AP_AMLPRMD ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x24)))
+#define _reg_CRM_AP_AMLPRME1 ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x28)))
+#define _reg_CRM_AP_AMLPRME2 ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x2c)))
+
+#define _reg_CRM_AP_AMLPMRF ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x30)))
+#define _reg_CRM_AP_AMLPMRG ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x34)))
+#define _reg_CRM_AP_APGCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x38)))
+#define _reg_CRM_AP_ACSR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x3c)))
+
+#define _reg_CRM_AP_ADCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x40)))
+#define _reg_CRM_AP_ACR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x44)))
+#define _reg_CRM_AP_AMCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x48)))
+#define _reg_CRM_AP_APCR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x4c)))
+
+#define _reg_CRM_AP_AMORA ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x50)))
+#define _reg_CRM_AP_AMORB ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x54)))
+#define _reg_CRM_AP_AGPR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x58)))
+#define _reg_CRM_AP_APRA ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x5c)))
+
+#define _reg_CRM_AP_APRB ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x60)))
+#define _reg_CRM_AP_APOR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x64)))
+
+
+//#define _reg_CRM_AP_APOR ((volatile __u32 *)(IO_ADDRESS(CRM_AP_BASE_ADDR + 0x64)))
+
+#define BCTRL_BASE_ADDR 0xb4000000
+#define _reg_BCTRL_VERSION ((volatile __u16 *)(IO_ADDRESS(BCTRL_BASE_ADDR + 0x0)))
+#define _reg_BCTRL_STATUS ((volatile __u16 *)(IO_ADDRESS(BCTRL_BASE_ADDR + 0x2)))
+#define _reg_BCTRL_1 ((volatile __u16 *)(IO_ADDRESS(BCTRL_BASE_ADDR + 0xa)))
+#define _reg_BCTRL_2 ((volatile __u16 *)(IO_ADDRESS(BCTRL_BASE_ADDR + 0xa)))
+
+#define BCTRL_2_USBSP (1 << 9)
+#define BCTRL_2_USBSD (1 << 10)
+
+
+#endif /* defined(CONFIG_MACH_SCMA11EVB) || defined(CONFIG_MACH_SCMA11BB) || defined(CONFIG_MACH_ARGONPLUSEVB) */
diff --git a/drivers/otg/hardware/mxc-ocd.c b/drivers/otg/hardware/mxc-ocd.c
new file mode 100644
index 000000000000..16636c22eff2
--- /dev/null
+++ b/drivers/otg/hardware/mxc-ocd.c
@@ -0,0 +1,692 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-ocd.c -- USB Device Controller driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/mxc/mxc-ocd.c|20070612233038|32668
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/mxc-ocd.c
+ * @brief Freescale USB OTG Controller Driver
+ *
+ * This is the OTG Controller Driver. It does generic configuration
+ * and handles the all USBOTG interrupts.
+ *
+ * There is no board or platform level code here.
+ *
+ * @ingroup FSOTG
+ * @ingroup OCD
+ *
+ */
+
+#include <otg/pcd-include.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+int mxc_transceiver_mode;
+
+/* ********************************************************************************************* */
+#define TIMEOUT_VALUE 1000
+/*!
+ * mxc_func_clock_on() - enable function controller clock
+ */
+void mxc_func_clock_on(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ fs_orl(OTG_CORE_CLK_CTRL, MODULE_FUNC_CLK);
+ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_FUNC_CLK)) { timeout--; if (!timeout) break; }
+
+ //TRACE_MSG1(OCD, "FUNC CLOCK ON: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+
+ //fs_orl(OTG_CORE_CINT_STEN, MODULE_ASFCINT_EN |MODULE_FCINT_EN);
+}
+
+/*!
+ * mxc_host_clock_on() - enable host controller clock
+ */
+void mxc_host_clock_on(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ fs_orl(OTG_CORE_CLK_CTRL, MODULE_HOST_CLK);
+ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_HOST_CLK)) { timeout--; if (!timeout) break; }
+ //TRACE_MSG1(OCD, "HOST CLOCK ON: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+}
+
+/*!
+ * mxc_func_clock_off() - disable function controller clock
+ */
+void mxc_func_clock_off(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ fs_andl(OTG_CORE_CLK_CTRL, 0x0);
+ while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_FUNC_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; }
+ //TRACE_MSG1(OCD, "FUNC CLOCK OFF: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+}
+
+/*!
+ * mxc_host_clock_off() - disable host controller clock
+ */
+void mxc_host_clock_off(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ fs_andl(OTG_CORE_CLK_CTRL, 0x0);
+ while((fs_rl(OTG_CORE_CLK_CTRL) & (MODULE_HOST_CLK | MODULE_MAIN_CLK))) { timeout--; if (!timeout) break; }
+ //TRACE_MSG1(OCD, "HOST CLOCK OFF: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+}
+
+/*!
+ * mxc_main_clock_on() - enable main clock
+ */
+void mxc_main_clock_on(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ fs_orl(OTG_CORE_CLK_CTRL, MODULE_MAIN_CLK);
+ while(!( fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; }
+ //TRACE_MSG1(OCD, "MAIN CLOCK ON: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+}
+
+/*!
+ * mxc_main_clock_off() - disable main clock
+ */
+void mxc_main_clock_off(void)
+{
+ u32 timeout = TIMEOUT_VALUE;
+ //fs_wl_set(OCD, OTG_CORE_CLK_CTRL, 0);
+ fs_wl(OTG_CORE_CLK_CTRL, 0);
+ while((fs_rl(OTG_CORE_CLK_CTRL) & MODULE_MAIN_CLK)) { timeout--; if (!timeout) break; }
+ //TRACE_MSG1(OCD, "MAIN CLOCK OFF: %08x", fs_rl(OTG_CORE_CLK_CTRL));
+}
+
+/*!
+ * mxc_set_transceiver_mode(int)
+ * @param mode - mode to set
+ */
+void mxc_set_transceiver_mode(int mode)
+{
+ mxc_transceiver_mode = mode;
+ fs_andl(OTG_CORE_HWMODE, ~0xf0);
+ fs_orl(OTG_CORE_HWMODE, (mode << 6) | (mode << 4)); // set to software hnp
+ //TRACE_MSG2(OCD, "set hwmode: %08x want %08x", fs_rl(OTG_CORE_HWMODE), (mode << 6) | (mode << 4));
+ //TRACE_MSG1(OCD, "hwmode: %08x", fs_rl(OTG_CORE_HWMODE));
+}
+
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * mxc_disable_interrupts() - disable interrupts
+ */
+void mxc_disable_interrupts (void)
+{
+ fs_wl(OTG_CORE_CINT_STEN, 0 );
+ fs_wl(OTG_CORE_HINT_STEN, 0 );
+ fs_wl(OTG_HOST_SINT_STEN, 0 );
+ fs_wl(OTG_HOST_XYINT_STEN, 0 );
+ fs_wl(OTG_HOST_ETD_EN, 0);
+ fs_wl(OTG_HOST_ETD_DONE, 0 );
+ fs_wl(OTG_FUNC_SINT_STEN, 0 );
+ fs_wl(OTG_FUNC_XYINT_STEN, 0 );
+ fs_wl(OTG_FUNC_EP_EN, 0 );
+ fs_wl(OTG_FUNC_EP_DEN, 0 );
+}
+
+/* ********************************************************************************************* */
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_init() - initial tcd setup
+ * Allocate interrupts and setup hardware.
+ * @param otg - otg instance
+ */
+void mxc_init (struct otg_instance *otg)
+{
+ int timeout;
+ unsigned long flags;
+ //u32 mode = XCVR_D_SE0;
+ u32 mode = XCVR_SE0_D_NEW;
+
+ TRACE_MSG0(otg->ocd->TAG, "FS_INIT");
+ local_irq_save (flags);
+
+ fs_wl(OTG_SYS_CTRL, 0x0);
+
+ /* 2. Ensure hardware is reset and cleared
+ */
+ // XXX
+ //fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_DMA_BASE), (32*16/4));
+ fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_DMA_BASE), (16*16/4));
+ fs_clear_words((void *)IO_ADDRESS(OTG_FUNC_BASE), 0x200);
+ mxc_main_clock_off();
+
+ //fs_wl_set(OCD, OTG_CORE_RST_CTRL, MODULE_RSTI2C | 0x3f);
+ fs_wl(OTG_CORE_RST_CTRL, MODULE_RSTI2C | 0x3f);
+ while (fs_rl(OTG_CORE_RST_CTRL));
+
+ /* 3. OTG Hardware Mode and clocks
+ * set to diff, diff and Configure the OTG to behave as function
+ */
+ TRACE_MSG0(otg->ocd->TAG, "3. OTG Software Mode and clock");
+
+ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_SHNP); // set to software hnp
+ mxc_set_transceiver_mode(mxc_transceiver_mode);
+ TRACE_MSG2(otg->ocd->TAG, "FS_INIT: set hwmode: %08x want %08x", fs_rl(OTG_CORE_HWMODE), MODULE_CRECFG_SHNP);
+
+ fs_andl(OTG_CORE_HNP_CSTAT, ~0x00000800);
+ fs_rl(OTG_CORE_HNP_CSTAT);
+
+ //fs_wl_set(OCD, OTG_CORE_HNP_T3PCR, 0x00000000);
+ fs_wl(OTG_CORE_HNP_T3PCR, 0x00000000);
+ fs_rl(OTG_CORE_HNP_T3PCR);
+
+ TRACE_MSG0(otg->ocd->TAG, "6. Enable ");
+ TRACE_MSG0(otg->ocd->TAG, "enable core interrupts");
+
+ fs_wl(OTG_CORE_CINT_STEN, 0);
+
+ fs_wl(OTG_CORE_CINT_STEN, MODULE_ASHNPINT_EN |
+ MODULE_ASHCINT_EN | MODULE_HNPINT_EN | MODULE_FCINT | MODULE_HCINT);
+
+
+ TRACE_MSG0(otg->ocd->TAG, "enable host interrupts");
+ fs_wl(OTG_CORE_HINT_STEN, HNP_I2COTGINT_EN | HNP_AWAITBTO_EN |
+ HNP_AIDLEBDTO_EN | HNP_SRPSUCFAIL_EN | HNP_SRPINT_EN | HNP_VBUSERROR_EN |
+ HNP_ABSEVAILD_EN | HNP_ABUSVALID_EN | HNP_MASSLVCHG_EN | HNP_IDCHANGE_EN);
+
+ TRACE_MSG0(otg->ocd->TAG, "disable various host interrupts");
+ fs_wl(OTG_HOST_XYINT_STEN, 0);
+ fs_wl(OTG_HOST_ETD_EN, 0);
+ fs_wl(OTG_HOST_ETD_DONE, 0);
+ fs_wl(OTG_HOST_SINT_STEN, 0);
+
+ TRACE_MSG0(otg->ocd->TAG, "disable various function interrupts");
+ fs_wl(OTG_FUNC_XYINT_STEN, 0);
+ fs_wl(OTG_FUNC_EP_EN, 0);
+ fs_wl(OTG_FUNC_EP_DEN, 0);
+
+ fs_wl(OTG_DMA_DINT_STEN, 0x3);
+ //fs_wb(I2C_MASTER_INT_REG_ADD, 0xf0);
+ //fs_wb(I2C_MASTER_INT_REG_ADD, 0x00);
+
+ // XXX note that newer designs than the mx21 will also need to check
+ // and/or set OTG_CORE_INTERRUPT_STEN
+
+
+ TRACE_MSG0(otg->ocd->TAG, "--");
+ TRACE_MSG0(otg->ocd->TAG, "8. Ready ");
+ TRACE_MSG1(otg->ocd->TAG, "CINT_STEN: %08x", fs_rl(OTG_CORE_CINT_STEN));
+
+ local_irq_restore (flags);
+}
+
+extern void mxc_stop_ep(int epn, int dir);
+
+/*!
+ * mxc_exit() - de-initialize
+ */
+void mxc_exit(struct otg_instance *otg)
+{
+ int i;
+ unsigned long flags;
+ TRACE_MSG0(otg->ocd->TAG, "FS_EXIT");
+
+ //mxc_disable_interrupts();
+ //_reg_CRM_PCCR1 &= ~(1<<27); // disable GPT3
+
+ local_irq_save (flags);
+ for (i = 0; i < 16; i++) {
+ mxc_stop_ep(i, USB_DIR_IN);
+ mxc_stop_ep(i, USB_DIR_OUT);
+ }
+ fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_EP_BASE), (32*16/4));
+ //fs_clear((void *)IO_ADDRESS(OTG_EP_BASE), 0x200);
+ //fs_clear((volatile u32 *)IO_ADDRESS(OTG_DMA_BASE), 32*16);
+ //fs_clear((void *)IO_ADDRESS(OTG_FUNC_BASE), 0x200);
+ fs_wl( OTG_CORE_HWMODE, 0xa3);
+ mxc_main_clock_off();
+ local_irq_restore (flags);
+}
+
+
+/*!
+ * mxc_ocd_init() - used to initialize/enable or disable the tcd driver
+ * @param otg
+ * @param flag
+ */
+void mxc_ocd_init(struct otg_instance *otg, u8 flag)
+{
+ TRACE_MSG0(otg->ocd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->ocd->TAG, "FS_otg->ocd->TAG_EN SET");
+ mxc_init(otg);
+ break;
+ case RESET:
+ mxc_exit(otg);
+ TRACE_MSG0(otg->ocd->TAG, "FS_otg->ocd->TAG_EN RESET");
+ break;
+ }
+ otg_event(otg, OCD_OK, otg->ocd->TAG, "MX21 OK");
+}
+
+/* ********************************************************************************************* */
+
+
+struct ocd_ops ocd_ops;
+
+extern irqreturn_t mxc_pcd_int_hndlr (void);
+extern irqreturn_t mxc_hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs);
+
+extern void mxc_func_clock_on(void);
+extern void mxc_func_clock_off(void);
+extern void mxc_main_clock_on(void);
+extern void mxc_main_clock_off(void);
+extern void mxc_set_hw_mode(int);
+
+#if defined(CONFIG_OTG_USB_PERIPHERAL) || defined(CONFIG_OTG_BDEVICE_WITH_SRP)
+irqreturn_t mxc_hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ return IRQ_HANDLED;
+}
+#endif /* defined(CONFIG_OTG_USB_PERIPHERAL) || defined(CONFIG_OTG_BDEVICE_WITH_SRP) */
+
+
+/* ********************************************************************************************* */
+/*!
+ * ocd_hnp_int_hndlr() - HNP interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t ocd_hnp_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 hint_stat = fs_rl(OTG_CORE_HINT_STAT);
+ u32 hnp_cstat = fs_rl(OTG_CORE_HNP_CSTAT);
+
+ /* get and clear interrupts
+ */
+ //fs_wl_set(OCD, OTG_CORE_HINT_STAT, hint_stat);
+ fs_wl(OTG_CORE_HINT_STAT, hint_stat);
+ printk(KERN_INFO"%s: HINT_STAT: %08x HNP_CSTAT: %08x\n", __FUNCTION__, hint_stat, hnp_cstat);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * pcd_bwkup_int_hndlr() - wakeup interrupt
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+static irqreturn_t pcd_bwkup_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 sys_ctrl = fs_rl(OTG_SYS_CTRL);
+ //static int bwkup_count = 0;
+
+ //TRACE_MSG2(otg->ocd->TAG, "SYS_CTRL: %08x CORE_CLK: %08x", sys_ctrl, fs_rl(OTG_CORE_CLK_CTRL));
+ //printk(KERN_INFO"%s: %08x CORE_CLK: %08x\n", __FUNCTION__, sys_ctrl, fs_rl(OTG_CORE_CLK_CTRL));
+
+ //fs_wl(OTG_SYS_CTRL, sys_ctrl & ~0x0F000000);
+ mxc_main_clock_on(); // turn on Main Clock
+ mxc_func_clock_on(); // turn on Function Clock
+
+ //if (bwkup_count++ > 10)
+ // fs_wl(OTG_SYS_CTRL, 0);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * ocd_dma_int_hndlr() - DMA interrupt
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+static irqreturn_t ocd_dma_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 dint_stat = fs_rl(OTG_DMA_DINT_STAT);
+ u32 etd_err = fs_rl(OTG_DMA_ETD_ERR);
+ u32 ep_err = fs_rl(OTG_DMA_EP_ERR);
+
+ static u32 dma_interrupts = 0;
+
+ dma_interrupts += 1;
+
+ printk(KERN_INFO"%s: dint_stat: %08x etd_err: %08x ep_err: %08x\n", __FUNCTION__, dint_stat, etd_err, ep_err);
+
+ if (dma_interrupts > 10) {
+ //TRACE_MSG1(otg->ocd->TAG, "DMA INT #%08x disabling DMA error interrupts",dint_stat);
+ fs_wl(OTG_DMA_DINT_STEN, 0x0);
+ fs_wl(OTG_DMA_EP_ERR, 0x0);
+ }
+
+ //TRACE_MSG3(otg->ocd->TAG, "dint_stat: %08x etd_err: %08x ep_err: %08x", dint_stat, etd_err, ep_err);
+
+ if (dint_stat) fs_wl(OTG_DMA_DINT_STAT, dint_stat);
+ if (etd_err) fs_wl(OTG_DMA_ETD_ERR, etd_err);
+ if (ep_err) fs_wl(OTG_DMA_EP_ERR, ep_err);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * hcd_host_int_hndlr() - Host interrupt
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+static irqreturn_t hcd_host_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 sint_stat = fs_rl(OTG_HOST_SINT_STAT);
+ static u32 hcd_interrupts = 0;
+ //printk(KERN_INFO"%s: %d\n", __FUNCTION__, hcd_interrupts++);
+ mxc_hcd_hw_int_hndlr(irq, NULL, regs);
+ return IRQ_HANDLED;
+}
+
+/*!
+ * ocd_ctrl_int_hndlr - Control and I2C interrupt
+ * Process USBOTG related interrupts.
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+static irqreturn_t ocd_ctrl_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 sys_ctrl = fs_rl(OTG_SYS_CTRL); // C.f. 23.8 USB Control Register
+ u32 cint_stat = fs_rl(OTG_CORE_CINT_STAT); // C.f. 23.9.2 USBOTG Module Interrupt Status Register
+ u32 hint_stat = fs_rl(OTG_CORE_HINT_STAT); // C.f. 23.9.12 HNP Interrupt Status Register
+ u32 hnp_cstat = fs_rl(OTG_CORE_HNP_CSTAT);
+
+ static u32 ocd_interrupts = 0;
+
+ /* N.B. OTG_CORE_CINT_STAT interrupts are self clearing when interrupt
+ * sources have been cleared.
+ */
+ RETURN_IRQ_HANDLED_UNLESS(cint_stat || hint_stat);
+
+ //if (sys_ctrl & (SYS_CTRL_HOST_WU_INT_STAT | SYS_CTRL_FNT_WU_INT_STAT))
+ // TRACE_MSG1(OCD, "SYS_CTRL: HOST/FUNC %08x", sys_ctrl);
+
+ //TRACE_MSG4(OCD, "CLK: %08x sys: %08x cint: %08x hint: %08x", fs_rl(OTG_CORE_CLK_CTRL), sys_ctrl, cint_stat, hint_stat);
+
+
+ if (hint_stat) {
+ //if (hint_stat & HNP_I2COTGINT)
+ // TRACE_MSG1(OCD, "OTG_CORE_HINT_STAT HNP_I2COTGINT %08x ignored (periodic?)", hint_stat);
+
+ fs_wl(OTG_CORE_HINT_STAT, hint_stat);
+ printk(KERN_INFO"%s: HINT_STAT: %08x HNP_CSTAT: %08x\n", __FUNCTION__, hint_stat, hnp_cstat);
+ }
+
+ if (cint_stat & MODULE_ASHNPINT) { // asynchronous HNP interrupt, enable Main clock
+ u32 hnp_cstat = fs_rl(OTG_CORE_HNP_CSTAT);
+ u32 hint_stat = fs_rl(OTG_CORE_HINT_STAT);
+ //TRACE_MSG1(OCD, "MODULE_ASHNPINT %08x", hint_stat);
+
+ mxc_main_clock_on(); // turn on Main Clock
+
+ //if (hnp_cstat & MODULE_ISBDEV)
+ // TRACE_MSG0(OCD, "ISBDEV");
+
+ //if (hnp_cstat & MODULE_ISADEV)
+ // TRACE_MSG0(OCD, "ISADEV");
+
+ //fs_wl_set(OCD, OTG_CORE_HINT_STAT, hint_stat);
+ fs_wl(OTG_CORE_HINT_STAT, hint_stat);
+ ocd_hnp_int_hndlr(irq, dev_id, regs);
+ }
+ if (cint_stat & MODULE_ASFCINT) { // Asynchronous Function interrupt, enable Func clock
+ //TRACE_MSG1(OCD, "MODULE_ASFCINT %08x", cint_stat);
+ // XXX otg_queue_event(ocd_instance->otg, B_SESS_VLD | A_SESS_VLD, PCD, "MX2ADS ASFCINT");
+ mxc_func_clock_on(); // turn on Function Clock
+ }
+
+ if (cint_stat & MODULE_ASHCINT) { // Asynchronous Host interrupt, enable Host clock
+ //TRACE_MSG1(OCD, "MODULE_ASHCINT %08x", cint_stat);
+ mxc_host_clock_on(); // turn on Host Clock
+ }
+
+ if ((cint_stat & MODULE_HNPINT) || hint_stat) // HNP interrupt
+ ocd_hnp_int_hndlr(irq, dev_id, regs);
+
+ if (cint_stat & MODULE_FCINT)
+ mxc_pcd_int_hndlr();
+
+ if (cint_stat & MODULE_HCINT) {
+ //TRACE_MSG1(OCD, "MODULE_HCINT %08x", cint_stat);
+ mxc_hcd_hw_int_hndlr(irq, NULL, regs);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/* ********************************************************************************************* */
+/*!
+ * pcd_bwkup_int_hndlr_isr() - main bwkkup interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t pcd_bwkup_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return pcd_bwkup_int_hndlr (irq, dev_id, regs);
+}
+
+/*!
+ * ocd_hnp_int_hndlr_isr() - main ocd hnp interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t ocd_hnp_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return ocd_hnp_int_hndlr (irq, dev_id, regs);
+}
+
+/*!
+ * pcd_func_int_hndlr_isr() - main pcd function interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t pcd_func_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //TRACE_MSG1(OCD, "CLK: %08x", fs_rl(OTG_CORE_CLK_CTRL) );
+
+ return mxc_pcd_int_hndlr();
+}
+
+/*!
+ * hcd_host_int_hndlr_isr() - main hcd host interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t hcd_host_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return hcd_host_int_hndlr (irq, dev_id, regs);
+}
+
+/*!
+ * ocd_ctrl_hndlr_isr() - main ocd controller interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t ocd_ctrl_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return ocd_ctrl_int_hndlr (irq, dev_id, regs);
+}
+
+/*!
+ * ocd_dma_int_hndlr_isr() - main dma interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ */
+irqreturn_t ocd_dma_int_hndlr_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ //pcd_instance->otg->interrupts++;
+ //TRACE_MSG0(OCD, "--");
+ return ocd_dma_int_hndlr (irq, dev_id, regs);
+}
+
+/* ********************************************************************************************* */
+extern void mxc_ocd_init (struct otg_instance *otg, u8 flag);
+extern int mxc_ocd_mod_init (struct otg_instance *otg);
+extern void mxc_ocd_mod_exit (struct otg_instance *otg);
+
+#if defined(CONFIG_OTG_GPTR)
+int mxc_gptcr_start_timer(struct otg_instance *otg, int usec);
+#endif /* defined(CONFIG_OTG_GPTR) */
+#if defined(CONFIG_OTG_HRT)
+int mxc_hrt_start_timer(struct otg_instance *otg, int usec);
+//otg_tick_t mxc_hrt_trace_ticks (void);
+//otg_tick_t mxc_hrt_trace_elapsed(otg_tick_t *t1, otg_tick_t *t2);
+#endif /* defined(CONFIG_OTG_HRT) */
+
+//otg_tick_t l26_ocd_ticks (void);
+//otg_tick_t l26_ocd_elapsed(otg_tick_t *t1, otg_tick_t *t2);
+
+#if defined(CONFIG_OTG_GPTR)
+otg_tick_t mxc_gptcr_trace_ticks (void);
+otg_tick_t mxc_gptcr_trace_elapsed(otg_tick_t *t1, otg_tick_t *t2);
+#endif /* defined(CONFIG_OTG_GPTR) */
+#if defined(CONFIG_OTG_HRT)
+otg_tick_t mxc_hrt_trace_ticks (void);
+otg_tick_t mxc_hrt_trace_elapsed(otg_tick_t *t1, otg_tick_t *t2);
+#endif /* defined(CONFIG_OTG_HRT) */
+
+#if !defined(OTG_C99)
+struct ocd_ops ocd_ops;
+void mxc_ocd_global_init(void)
+{
+ ZERO(ocd_ops);
+ #if defined(CONFIG_OTG_TR_AUTO)
+ ocd_ops.capabilities = OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO;
+ #else
+ ocd_ops.capabilities = OCD_CAPABILITIES_TR;
+ #endif
+ ocd_ops.ocd_init_func = mxc_ocd_init;
+ ocd_ops.mod_init = mxc_ocd_mod_init;
+ ocd_ops.mod_exit = mxc_ocd_mod_exit;
+#if defined(CONFIG_OTG_GPTR)
+ ocd_ops.start_timer = mxc_gptcr_start_timer;
+ ocd_ops.ticks = mxc_gptcr_trace_ticks;
+ ocd_ops.elapsed = mxc_gptcr_trace_elapsed;
+#endif /* defined(CONFIG_OTG_GPTR) */
+#if defined(CONFIG_OTG_HRT)
+ ocd_ops.start_timer = mxc_hrt_start_timer;
+ ocd_ops.ticks = mxc_hrt_trace_ticks;
+ ocd_ops.elapsed = mxc_hrt_trace_elapsed;
+#endif /* defined(CONFIG_OTG_HRT) */
+}
+#else /* !defined(OTG_C99) */
+//void mxc_ocd_global_init(void) {
+
+//}
+struct ocd_ops ocd_ops = {
+ #if defined(CONFIG_OTG_TR_AUTO)
+ .capabilities = OCD_CAPABILITIES_TR | OCD_CAPABILITIES_AUTO,
+ #else
+ .capabilities = OCD_CAPABILITIES_TR,
+ #endif
+ .ocd_init_func = mxc_ocd_init,
+ .mod_init = mxc_ocd_mod_init,
+ .mod_exit = mxc_ocd_mod_exit,
+#if defined(CONFIG_OTG_GPTR)
+ .start_timer = mxc_gptcr_start_timer,
+ .ticks = l26_ocd_trace_ticks,
+ .elapsed = l26_ocd_trace_elapsed,
+#endif /* defined(CONFIG_OTG_GPTR) */
+#if defined(CONFIG_OTG_HRT)
+ .start_timer = mxc_hrt_start_timer,
+ .ticks = mxc_hrt_trace_ticks,
+ .elapsed = mxc_hrt_trace_elapsed,
+#endif /* defined(CONFIG_OTG_HRT) */
+};
+#endif /* !defined(OTG_C99) */
+
+/* ********************************************************************************************* */
+
+/*!
+ * mxc_ocd_mod_init() - initial tcd setup
+ * Allocate interrupts and setup hardware.
+ */
+int mxc_ocd_mod_init (struct otg_instance *otg)
+{
+ int bwkup = request_irq (INT_USB_WAKEUP, pcd_bwkup_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD BWKUP", NULL);
+ int func = request_irq (INT_USB_FUNC, pcd_func_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD FUNC", NULL);
+ int ctrl = request_irq (INT_USB_CTRL, ocd_ctrl_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD CTRL", NULL);
+ //int sof = request_irq (INT_USB_SOF, sof_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD SOF", NULL);
+ //int host = request_irq (OTG_USBHOST, hcd_host_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD HOST", NULL);
+ int host = 0;
+ int dma = request_irq (INT_USB_DMA, ocd_dma_int_hndlr_isr, OTG_INTERRUPT, UDC_NAME " USBD DMA", NULL);
+
+ TRACE_MSG0(otg->ocd->TAG, "1. Interrupts requested");
+
+ RETURN_EINVAL_IF(bwkup || dma || func || host || ctrl);
+
+ return 0;
+}
+
+extern void mxc_stop_ep(int epn, int dir);
+
+/*!
+ * mxc_ocd_mod_exit() - de-initialize
+ */
+void mxc_ocd_mod_exit (struct otg_instance *otg)
+{
+ #if 1
+ int i;
+ unsigned long flags;
+
+// fsptrcr_mod_exit();
+// _reg_CRM_PCCR1 &= ~(1<<27); // disable GPT3
+
+ TRACE_MSG0(otg->ocd->TAG, "free irqs");
+// for (i = INT_USB_WAKEUP; i <= INT_USB_DMA; i++) free_irq (i, NULL); // Free IRQ's
+ free_irq (INT_USB_WAKEUP, NULL);
+ free_irq (INT_USB_FUNC, NULL);
+ free_irq (INT_USB_CTRL, NULL);
+ free_irq (INT_USB_DMA, NULL);
+ //free_irq (INT_GPT2, NULL);
+ //free_irq (INT_GPIO, NULL);
+ #endif
+}
+
+
+/* ********************************************************************************************* */
diff --git a/drivers/otg/hardware/mxc-pcd.c b/drivers/otg/hardware/mxc-pcd.c
new file mode 100644
index 000000000000..4c3878af98cf
--- /dev/null
+++ b/drivers/otg/hardware/mxc-pcd.c
@@ -0,0 +1,1584 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-pcd.c -- Freescale USBOTG Peripheral Controller driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/mxc/mxc-pcd.c|20070822214154|21475
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/mxc-pcd.c
+ * @brief Freescale USB Peripheral Controller Driver
+ * This implements the Freescale USBOTG Peripheral Controller Driver.
+ *
+ * There is some processor specific code here to support the alternate processors
+ * that implement this type of USB support.
+ *
+ * There is no board or platform level code here.
+ *
+ * @ingroup FSOTG
+ * @ingroup PCD
+ *
+ *
+ * Notes
+ *
+ * 1. EP_RDY is a toggle register, not R/W.
+ *
+ * 2. There is an EP_RDY_CLR register (only for TO2?)
+ *
+ * 3. EP_TOGGLE is RW for TO1 and toggle for TO2.
+ *
+ * 4. DMA transfers always transfer buffersize amounts of data.
+ *
+ * 5. DMA IN transfers for very short (2 byte) transfers can take longer than the
+ * data being sent causing done status change to fail.
+ *
+ * 6. DMA IN transfers must be monitored and the DMA enable forcibly reset if it
+ * does not clear by itself.
+ *
+ */
+
+#include <otg/pcd-include.h>
+#if defined(CONFIG_OTG_LNX)
+#include "mxc-lnx.h"
+#endif /* defined(CONFIG_OTG_LNX) */
+#if defined(CONFIG_OTG_QNX)
+#include "mx21.h"
+#include "mxc-qnx.h"
+#endif /* defined(CONFIG_OTG_QNX) */
+#include "mxc-hardware.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+
+
+//#define SHORT_DMA_IN /* define this to force transfers to be maximum of 2*wMaxPacketSize */
+//#define NO_DMA_IN /* define this to use memcpy instead of DMA */
+
+//#define SHORT_DMA_OUT /* define this to force transfers to be maximum of 2*wMaxPacketSize */
+//#define NO_DMA_OUT /* define this to use memcpy instead of DMA */
+
+//#define MXC_ACTIVE /* active SOF checking */
+//#define MXC_TRACK_TIMER
+
+#define TRACE_VERBOSE 0
+#define TRACE_VERY_VERBOSE 0
+
+struct pcd_instance *mxc_pcd_instance;
+
+void mxc_func_clock_on(void); /* defined in mx2-ocd.c */
+void mxc_func_clock_off(void); /* defined in mx2-ocd.c */
+void mxc_main_clock_on(void);
+void mxc_main_clock_off(void);
+
+
+u8 mxc_new_address, mxc_usb_address; /* used to save and set USB address */
+u16 mxc_funcrev;
+
+#ifdef MXC_TRACK_TIMER
+void mxc_pcd_hrt_callback (unsigned long arg)
+{
+ struct usbd_endpoint_instance *endpoint=(struct usbd_endpoint_instance *) arg;
+ printk(KERN_INFO "\n");
+ printk(KERN_INFO"%s: The endpoint_instance got time Out while waiting Done interrupt-----urbs in queue:%ld\n", __FUNCTION__,(endpoint->rdy).total);
+}
+
+#endif
+
+/* ********************************************************************************************* */
+
+/* The EP Toggle register has different ways of setting and clearing
+ * depending on hardware version. The older hardware is R/W. The newer
+ * hardware is write to toggle the current value.
+ */
+
+
+void mxc_halt_endpoint0_in(int pipe)
+{
+
+ u32 ep0_current, ep0_new;
+
+ ep0_current = fs_rl(ep_word(0, pipe, 0));
+ ep0_new = (1 << 31) | (ep0_current);
+
+ UNLESS (ep0_current == ep0_new) fs_wl(ep_word(0,pipe, 0), ep0_new);
+
+}
+
+/*! mxc_reset_toggles
+ */
+static void inline
+mxc_reset_toggles(void)
+{
+ //u32 toggle = fs_rl(OTG_FUNC_EP_TOGGLE);
+ if (mxc_funcrev == 0x11) {
+ /* TO1 - RW register, simply or the bit and write
+ */
+ fs_wl(OTG_FUNC_EP_TOGGLE, 0);
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG2(mxc_pcd_instance->TAG, "EP_TOGGLES: %08x -> %08x (1.1)", toggle, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+ else {
+ /* TO2 - RC - check and then write the bit to toggle to the other value if required
+ */
+ fs_wl(OTG_FUNC_EP_TOGGLE, fs_rl(OTG_FUNC_EP_TOGGLE));
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG2(mxc_pcd_instance->TAG, "EP_TOGGLES: %08x -> %08x (2.0)", toggle, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+}
+
+/*! mxc_reset_toggle_ep
+ */
+static void inline
+mxc_reset_toggle_ep(int ep_num)
+{
+ //u32 toggle = fs_rl(OTG_FUNC_EP_TOGGLE);
+ if (mxc_funcrev == 0x11) {
+ /* TO1 - RW register, simply or the bit and write
+ */
+ fs_andl(OTG_FUNC_EP_TOGGLE, ~ep_num);
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG3(mxc_pcd_instance->TAG, "EP_TOGGLE[%d]: %08x -> %08x (1.1)",
+ // toggle, ep_num, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+ else {
+ /* TO2 - RC - check and then write the bit to toggle to the other value if required
+ */
+ if (fs_rl(OTG_FUNC_EP_TOGGLE) & ep_num) fs_wl(OTG_FUNC_EP_TOGGLE, ep_num);
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG3(mxc_pcd_instance->TAG, "EP_TOGGLE[%d]: %08x -> %08x (2.0)",
+ // toggle, ep_num, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+}
+
+/*! mxc_set_toggle_ep
+ */
+static void inline
+mxc_set_toggle_ep(int ep_num)
+{
+ //u32 toggle = fs_rl(OTG_FUNC_EP_TOGGLE);
+ if (mxc_funcrev == 0x11) {
+ /* TO1 - RW register, simply or the bit and write
+ */
+ fs_orl(OTG_FUNC_EP_TOGGLE, ep_num);
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG2(mxc_pcd_instance->TAG, "EP OR: %08x -> %08x (1.1)", toggle, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+ else {
+ /* TO2 - RC - check and then write the bit to toggle to the other value if required
+ */
+ UNLESS (fs_rl(OTG_FUNC_EP_TOGGLE) & ep_num) fs_wl(OTG_FUNC_EP_TOGGLE, ep_num);
+ //if (TRACE_VERY_VERBOSE)
+ // TRACE_MSG2(mxc_pcd_instance->TAG, "EP TOGGLE: %08x -> %08x (2.0)", toggle, fs_rl(OTG_FUNC_EP_TOGGLE));
+ }
+}
+/* ********************************************************************************************* */
+/*
+ * N.B. EP_RDY is a toggle not RW register.
+ *
+ * XXX need to check what TO1 does. I think it is RW register.
+ */
+
+/*! mxc_reset_ep_rdy
+ */
+static void /* inline */
+mxc_reset_ep_rdy(int ep_num)
+{
+ u32 ep_rdy;
+ RETURN_UNLESS ((ep_rdy = fs_rl(OTG_FUNC_EP_RDY)) & ep_num);
+ if (mxc_funcrev == 0x11) {
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "Toggle: ep_rdy: %08x", fs_rl(OTG_FUNC_EP_RDY_CLR));
+ fs_wl(OTG_FUNC_EP_RDY, ep_num); // toggle ready bit
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "Toggle: ep_rdy: %08x", fs_rl(OTG_FUNC_EP_RDY_CLR));
+ }
+ else {
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "Clear: ep_rdy: %08x", fs_rl(OTG_FUNC_EP_RDY_CLR));
+ fs_wl(OTG_FUNC_EP_RDY_CLR, ep_num); // clear ready bit
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "Clear: ep_rdy: %08x", fs_rl(OTG_FUNC_EP_RDY_CLR));
+ }
+}
+
+/*! mxc_set_ep_rdy
+ *
+ * N.B. EP_RDY is a toggle not RW register.
+ */
+static void /* inline */
+mxc_set_ep_rdy(int ep_num)
+{
+ u32 ep_rdy;;
+ RETURN_IF ((ep_rdy = fs_rl(OTG_FUNC_EP_RDY)) & ep_num);
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_EP_RDY, ep_num); // set ready bit
+}
+/* ********************************************************************************************* */
+/*
+ * These track IN endpoint transfers and enable the SOF interrupt
+ * when (if) there is a transfer pending in any IN endpoint
+ */
+#ifdef MXC_ACTIVE
+static u32 mxc_pcd_active;
+#endif /* MXC_ACTIVE */
+
+/*! mxc_sof_active
+ */
+void mxc_sof_active(u32 ep_num)
+{
+ #ifdef MXC_ACTIVE
+ /* enable SOF interrupt if nothing is currently active */
+ UNLESS (mxc_pcd_active)
+ fs_orl(OTG_FUNC_SINT_STEN, SYSTEM_SOFDETINT_EN); // XXX ORL - ok
+ mxc_pcd_active |= ep_num;
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "ep_num: %08x active: %08x sint_sten: %08x",
+ ep_num, mxc_pcd_active, fs_rl(OTG_FUNC_SINT_STEN));
+ #endif /* MXC_ACTIVE */
+}
+
+/*! mxc_sof_cancel
+ */
+void mxc_sof_cancel(u32 ep_num)
+{
+ #ifdef MXC_ACTIVE
+ /* disable SOF interrupt if nothing is now active */
+ mxc_pcd_active &= ~ep_num;
+ UNLESS (mxc_pcd_active)
+ fs_wl(OTG_FUNC_SINT_STEN_CLR, SYSTEM_SOFDETINT_EN);
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "ep_num: %08x active: %08x sint_sten: %08x",
+ ep_num, mxc_pcd_active, fs_rl(OTG_FUNC_SINT_STEN));
+ #endif /* MXC_ACTIVE */
+}
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * mxc_ep_config() - configure and enable an endpoint to send or receive data
+ * First setup the endpoint descriptor words, and then enable the endpoint and done interrupt
+ * @param epn - endpoint number
+ * @param dir - direction IN or OUT
+ * @param ep_num -
+ * @param wMaxPacketSize
+ * @param buffersize
+ * @param format
+ * @param ttlbtecnt
+ * @param stall
+ *
+ * Adjust buffersize to match transfersize for IN transfers less than 2 *
+ * packet size. Note that this requires that the x/y buffers are
+ * sequentially allocated. Effectively this makes the DMA only transfer the
+ * exact amount of the transfer.
+ */
+static void inline
+mxc_ep_config(int epn, int dir, u32 ep_num, int wMaxPacketSize, int buffersize, int format, int ttlbtecnt, int stall)
+{
+ u32 ep_en = fs_rl(OTG_FUNC_EP_EN);
+ u32 ep0_current = fs_rl(ep_word(epn, dir, 0));
+ u32 ep1_current = fs_rl(ep_word(epn, dir, 1));
+ u32 ep3_current = fs_rl(ep_word(epn, dir, 3));
+
+ u32 ep0_new = ((stall ? 1 : 0) << 31) | (wMaxPacketSize << 16) | ((format & 3) << 14);
+ u32 ep1_new = ttlbtecnt ? (data_y_buf(epn, dir) << 16) | data_x_buf(epn, dir): 0;
+ u32 ep3_new = ((buffersize - 1) << 21) | (ttlbtecnt & 0x1fffff);
+
+
+ if (TRACE_VERBOSE) {
+ TRACE_MSG7(mxc_pcd_instance->TAG, "epn[%02x:%02x] ep_num: %08x sz: %d fmt: %x cnt: %d stall: %d",
+ epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, stall);
+ TRACE_MSG7(mxc_pcd_instance->TAG, "ep_en: %08x ep0[%08x:%08x] ep1[%08x:%08x] ep3[%08x:%08x]", ep_en,
+ ep0_current, ep0_new, ep1_current, ep1_new, ep3_current, ep3_new);
+ TRACE_MSG2(mxc_pcd_instance->TAG, "xfill: %08x yfill: %08x",
+ fs_rl(OTG_FUNC_XFILL_STAT), fs_rl(OTG_FUNC_YFILL_STAT));
+ }
+
+ /* disable interrupts to ensure that all changes to endpoint
+ * configuration are completed with minimal latency
+ */
+ otg_disable_interrupts();
+
+ UNLESS (ep0_current == ep0_new) fs_wl(ep_word(epn, dir, 0), ep0_new);
+ UNLESS (ep1_current == ep1_new) fs_wl(ep_word(epn, dir, 1), ep1_new);
+ UNLESS (ep3_current == ep3_new) fs_wl(ep_word(epn, dir, 3), ep3_new);
+ UNLESS(ep_en & ep_num) fs_orl(OTG_FUNC_EP_EN, ep_num); // enable the endpoint for use // XXX or required mx21
+
+ fs_orl(OTG_FUNC_IINT, ep_num); // issue done status interrupts immediately
+ fs_orl(OTG_FUNC_EP_DEN, ep_num); // enable endpoint done interrupt
+
+ otg_enable_interrupts();
+
+ /* interrupts enabled again */
+
+ if (TRACE_VERBOSE) {
+ TRACE_MSG7(mxc_pcd_instance->TAG, "epn[%02x:%02x] %08x %08x %08x TOG: %08x EP_EN: %08x", epn, dir,
+ fs_rl(ep_word(epn, dir, 0)), fs_rl(ep_word(epn, dir, 1)),
+ fs_rl(ep_word(epn, dir, 3)), fs_rl(OTG_FUNC_EP_TOGGLE), fs_rl(OTG_FUNC_EP_EN));
+
+ TRACE_MSG7(mxc_pcd_instance->TAG, "epn[%02x:%02x] ep_num: %08x sz: %d fmt: %x cnt: %d stall: %d",
+ epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, stall);
+ }
+#if 0
+ printk (KERN_INFO"%s: epn[%02x:%02x] %08x %08x %08x TOG: %08x EP_EN: %08x\n", __FUNCTION__,
+ epn, dir, fs_rl(ep_word(epn, dir, 0)), fs_rl(ep_word(epn, dir, 1)),
+ fs_rl(ep_word(epn, dir, 3)), fs_rl(OTG_FUNC_EP_TOGGLE), fs_rl(OTG_FUNC_EP_EN));
+ printk (KERN_INFO"%s: epn[%02x:%02x] ep_num: %08x sz: %d fmt: %x cnt: %d stall: %d\n", __FUNCTION__,
+ epn, dir, ep_num, wMaxPacketSize, format, ttlbtecnt, stall);
+#endif
+}
+
+/*!
+ * mxc_ep_config_nodma() - configure and enable an endpoint to send or receive data
+ * Non-DMA setup, optional stall, ready etc.
+ * @param epn - endpoint number
+ * @param dir
+ * @param stall - stall endpoint
+ * @param wMaxPacketSize
+ * @param format
+ * @param ttlbtecnt
+ * @param rdy1
+ * @param rdy2
+ */
+static void
+mxc_ep_config_nodma(int epn, int dir, int stall, int wMaxPacketSize, int format, int ttlbtecnt, int rdy1, int rdy2)
+{
+ u32 ep_num = ep_num_dir(epn, dir);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG5(mxc_pcd_instance->TAG, "EP CONFIG NODMA: epn[%02x] num: %02x stall: %d r1: %d r2: %d",
+ epn, ep_num, stall, rdy1, rdy2);
+ if (rdy1) {
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "EP CONFIG NODMA: epn: %02x num: %02x checking RDY: %02x",
+ epn, ep_num, rdy1 && fs_rl(OTG_FUNC_EP_RDY) & ep_num);
+ RETURN_IF (rdy1 && (fs_rl(OTG_FUNC_EP_RDY) & ep_num));
+ }
+
+ //UNLESS(epn)
+ // fs_orl(OTG_FUNC_XYINT_STEN, ep_num); // enable x or y buffer interrupt
+
+ mxc_ep_config(epn, dir, ep_num, wMaxPacketSize, wMaxPacketSize, format, ttlbtecnt, stall);
+ RETURN_UNLESS(rdy2);
+
+ mxc_set_ep_rdy(ep_num);
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG4(mxc_pcd_instance->TAG, "IINT: %08x DEN: %08x EN: %08x RDY: %08x", fs_rl(OTG_FUNC_IINT),
+ fs_rl(OTG_FUNC_EP_DEN), fs_rl(OTG_FUNC_EP_EN), fs_rl(OTG_FUNC_EP_RDY));
+}
+
+/*!
+ * mxc_pcd_start_ep0_setup() - enable ep0 for receiving SETUP request
+ * Unless already ready, configure ep0 to receive.
+ * @param endpoint - endpoint instance
+ */
+static void inline
+mxc_pcd_start_ep0_setup (struct usbd_endpoint_instance *endpoint)
+{
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG0(mxc_pcd_instance->TAG, "START EP0: config");
+
+ // XXX rdy1 must be FALSE for OUT and TRUE for IN
+ mxc_ep_config_nodma(0, USB_DIR_OUT, 0, endpoint->wMaxPacketSize[0], EP_FORMAT_CONTROL,
+ endpoint->wMaxPacketSize[0], 0, 1);
+ mxc_ep_config_nodma(0, USB_DIR_IN, 0, endpoint->wMaxPacketSize[0], EP_FORMAT_CONTROL, 0, 1, 0);
+}
+
+/*!
+ * mxc_sendzlp() - start sending a buffer on a specified endpoint
+ * Toggle XFILL_STAT or YFILL_STAT bit, then call mxc_ep_config_nodma().
+ * @param epn - endpoint number
+ * @param endpoint - endpoint instance
+ * @param wMaxPacketSize
+ */
+static void inline
+mxc_sendzlp (int epn, struct usbd_endpoint_instance *endpoint, int wMaxPacketSize)
+{
+ u32 ep_num_in = ep_num_in(epn);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG0(mxc_pcd_instance->TAG, "ZLP");
+ if (!(fs_rl(OTG_FUNC_XFILL_STAT) & ep_num_in))
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_XFILL_STAT, ep_num_in);
+ else if (!(fs_rl(OTG_FUNC_YFILL_STAT) & ep_num_in))
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_YFILL_STAT, ep_num_in);
+
+ mxc_ep_config_nodma(epn, USB_DIR_IN, 0, endpoint->wMaxPacketSize[0], endpoint->bmAttributes[0] & 0x3, 0, 1, 1);
+}
+
+/*!
+ * mxc_stop_ep() - stop endpoint
+ * @param epn - endpoint number
+ * @param dir - direction IN or OUT
+ */
+void mxc_stop_ep(int epn, int dir)
+{
+ int ep_num = ep_num_dir(epn, dir);
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG7(mxc_pcd_instance->TAG, "--> ep_rdy: %08x dma_ep: %08x iint: %08x int: %08x %08x fill: %08x %08x",
+ fs_rl(OTG_FUNC_EP_RDY), fs_rl(OTG_DMA_EP_CH_CLR), fs_rl(OTG_FUNC_IINT),
+ fs_rl(OTG_FUNC_XINT_STAT), fs_rl(OTG_FUNC_YINT_STAT),
+ fs_rl(OTG_FUNC_XFILL_STAT), fs_rl(OTG_FUNC_YFILL_STAT));
+
+ mxc_reset_ep_rdy(ep_num);
+
+ fs_wl(OTG_DMA_EP_CH_CLR, ep_num);
+
+ fs_wl_set(mxc_pcd_instance->TAG,OTG_FUNC_IINT_CLR, ep_num);
+
+ if (fs_rl(OTG_FUNC_XINT_STAT) & ep_num) {
+ fs_wl_clr(mxc_pcd_instance->TAG, OTG_FUNC_XINT_STAT, ep_num);
+ fs_wl_clr(mxc_pcd_instance->TAG, OTG_FUNC_XFILL_STAT, ep_num);
+ }
+ if (fs_rl(OTG_FUNC_YINT_STAT) & ep_num) {
+ fs_wl_clr(mxc_pcd_instance->TAG, OTG_FUNC_YINT_STAT, ep_num);
+ fs_wl_clr(mxc_pcd_instance->TAG, OTG_FUNC_YFILL_STAT, ep_num);
+ }
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_setup_ep() - setup endpoint
+ * @param pcd - pcd instance
+ * @param epn - endpoint number
+ * @param endpoint - endpoint instance
+ */
+static void inline
+mxc_pcd_setup_ep (struct pcd_instance *pcd, unsigned int epn, struct usbd_endpoint_instance *endpoint)
+{
+ u32 ep_num;
+ int dirr,epnn;
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG2(mxc_pcd_instance->TAG, "epn[%d] START EPN: config %02x", epn, endpoint->bEndpointAddress[0]);
+
+ /* check if not endpoint zero and we have an assigned address */
+ RETURN_UNLESS(epn && endpoint->bEndpointAddress[0]);
+
+ dirr=endpoint->bEndpointAddress[0] & USB_DIR_IN;
+ epnn=endpoint->bEndpointAddress[0] & 0x7f;
+ ep_num= ep_num_dir(epnn, dirr);
+
+ /* Start from DATA0 */
+ mxc_reset_toggle_ep(ep_num);
+
+ mxc_ep_config_nodma(endpoint->bEndpointAddress[0] & 0x7f, endpoint->bEndpointAddress[0] & USB_DIR_IN, 0,
+ endpoint->wMaxPacketSize[0], endpoint->bmAttributes[0] & 0x3, 0, 0, 0);
+
+
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_start_endpoint_out() - start receive
+ * @param pcd - pcd instance
+ * @param endpoint - endpoint number
+ */
+static void
+mxc_pcd_start_endpoint_out(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint);
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ u32 ep_num = ep_num_dir(epn, USB_DIR_OUT);
+ u32 dma_num = dma_num_dir(epn, USB_DIR_OUT);
+ UNLESS (rcv_urb) {
+ UNLESS (epn) mxc_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup
+ return;
+ }
+
+
+ #if defined(SHORT_DMA_OUT) || defined(NO_DMA_OUT)
+ endpoint->last = MIN(rcv_urb->buffer_length, 2 * endpoint->wMaxPacketSize[0]);
+ #else /* defined(SHORT_DMA_OUT) || defined(NO_DMA_OUT) */
+ endpoint->last = rcv_urb->buffer_length;
+ #endif /* defined(SHORT_DMA_OUT) || defined(NO_DMA_OUT) */
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(mxc_pcd_instance->TAG,
+ "epn[%02x] EP CONFIG DMA: num: %02x buffer: %p length: %d dir: %d rcv_urb: %x last: %d",
+ epn, ep_num, rcv_urb->buffer, rcv_urb->buffer_length,
+ USB_DIR_OUT, rcv_urb, endpoint->last);
+
+ mxc_ep_config(epn, USB_DIR_OUT, ep_num, endpoint->wMaxPacketSize[0],
+ endpoint->wMaxPacketSize[0], endpoint->bmAttributes[0] & 0x3, endpoint->last, 0);
+
+ #if !defined(NO_DMA_OUT)
+ CACHE_SYNC_RCV (rcv_urb->buffer + rcv_urb->actual_length, endpoint->last);
+ fs_wl(OTG_DMA_EPN_MSA(dma_num), virt_to_phys(rcv_urb->buffer + rcv_urb->actual_length));
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_DMA_EP_EN, ep_num);
+ #endif /* NO_DMA_OUT */
+ mxc_set_ep_rdy(ep_num);
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG2(mxc_pcd_instance->TAG, "epn[%02x] EP_RDY: %08x", epn, fs_rl(OTG_FUNC_EP_RDY));
+}
+
+/*!
+ * mxc_pcd_stop_out() - process interrupt for received buffer
+ * @param pcd - pcd instance
+ * @param epn - endpoint number
+ * @param endpoint - endpoint instance
+ */
+static void
+mxc_pcd_stop_out (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *rcv_urb = pcd_rcv_next_irq(endpoint);
+ u32 ep3 = fs_rl(ep_word(epn, USB_DIR_OUT, 3)) & 0x1fffff; // XXX fix mask
+ //u32 ep_num = ep_num_out(epn);
+ #if defined(NO_DMA_OUT)
+ int wMaxPacketSize = endpoint->wMaxPacketSize[0];
+ #endif /* defined(NO_DMA_OUT) */
+ int last;
+ int xfer;
+
+ mxc_stop_ep(epn, USB_DIR_OUT);
+
+ RETURN_UNLESS (rcv_urb); // XXX should reset fill bits...
+ last = endpoint->last;
+
+ xfer = last - ep3;
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG6(mxc_pcd_instance->TAG, "epn{%02x] actual: %d rcv_urb: %x last: %d ep3: %04x xfer: %d",
+ epn, rcv_urb->actual_length, rcv_urb, last, ep3, xfer);
+
+ #if defined(NO_DMA_OUT)
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(mxc_pcd_instance->TAG, "--> ep_rdy: %08x dma_ep: %08x iint: %08x int: %08x %08x fill: %08x %08x",
+ fs_rl(OTG_FUNC_EP_RDY), fs_rl(OTG_DMA_EP_CH_CLR), fs_rl(OTG_FUNC_IINT),
+ fs_rl(OTG_FUNC_XINT_STAT), fs_rl(OTG_FUNC_YINT_STAT),
+ fs_rl(OTG_FUNC_XFILL_STAT), fs_rl(OTG_FUNC_YFILL_STAT));
+
+ if (xfer /*&& (fs_rl(OTG_FUNC_XFILL_STAT) & ep_num)*/) {
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "DATA_X: dst: %x src: %x len: %d",
+ rcv_urb->buffer + rcv_urb->actual_length,
+ (void *)data_x_address(epn, USB_DIR_OUT), wMaxPacketSize);
+
+ memcpy(rcv_urb->buffer + rcv_urb->actual_length, (void *)data_x_address(epn, USB_DIR_OUT), wMaxPacketSize);
+
+ //pcd_rcv_complete_irq (endpoint, MIN(xfer, wMaxPacketSize), 0);
+ //fs_wl(OTG_FUNC_XFILL_STAT, ep_num); // XXX this should be necessary
+ }
+ if ((xfer > wMaxPacketSize) /*&& (fs_rl(OTG_FUNC_YFILL_STAT) & ep_num)*/) {
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "DATA_Y: dst: %x src: %x len: %d",
+ rcv_urb->buffer + rcv_urb->actual_length + wMaxPacketSize,
+ (void *)data_y_address(epn, USB_DIR_OUT), wMaxPacketSize);
+
+ memcpy(rcv_urb->buffer + rcv_urb->actual_length + wMaxPacketSize,
+ (void *)data_y_address(epn, USB_DIR_OUT), wMaxPacketSize);
+
+ //pcd_rcv_complete_irq (endpoint, MIN(xfer - wMaxPacketSize, wMaxPacketSize), 0);
+ //fs_wl(OTG_FUNC_YFILL_STAT, ep_num); // XXX this should be necessary
+ }
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(mxc_pcd_instance->TAG, "<-- ep_rdy: %08x dma_ep: %08x iint: %08x int: %08x %08x fill: %08x %08x",
+ fs_rl(OTG_FUNC_EP_RDY), fs_rl(OTG_DMA_EP_CH_CLR), fs_rl(OTG_FUNC_IINT),
+ fs_rl(OTG_FUNC_XINT_STAT), fs_rl(OTG_FUNC_YINT_STAT),
+ fs_rl(OTG_FUNC_XFILL_STAT), fs_rl(OTG_FUNC_YFILL_STAT));
+ #endif /* NO_DMA_OUT */
+
+ //pcd_rcv_finished_irq (endpoint, xfer, 0);
+ pcd_rcv_complete_irq (endpoint, xfer, 0);
+
+ if (!epn) mxc_sendzlp(epn, endpoint, endpoint->wMaxPacketSize[0]); // ZLP
+ else mxc_pcd_start_endpoint_out(pcd, endpoint); // restart
+}
+/*!
+ * mxc_pcd_cancel_out_irq() - cancel OUT urb
+ * @param pcd
+ * @param urb
+ */
+static void
+mxc_pcd_cancel_out_irq (struct pcd_instance *pcd,struct usbd_urb *urb)
+{
+ struct usbd_endpoint_instance *endpoint = urb->endpoint;
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn);
+ mxc_stop_ep(epn, USB_DIR_OUT);
+}
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_start_endpoint_in() - start transmit
+ * Get next tx_urb (completing old one if there is one) and start sending it.
+ * @param pcd - pcd instance
+ * @param endpoint - endpoint instance
+ */
+static void
+mxc_pcd_start_endpoint_in(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *tx_urb;
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ u32 ep_num = ep_num_dir(epn, USB_DIR_IN);
+ u32 dma_num = dma_num_dir(epn, USB_DIR_IN);
+ int wMaxPacketSize = endpoint->wMaxPacketSize[0];
+ //int i;
+ int remaining;
+#ifdef MXC_TRACK_TIMER
+ if (timer_pending(&(endpoint->pcd_hr_timer))){
+ TRACE_MSG0(mxc_pcd_instance->TAG, "Trying to reconfiure EP before previous finished");
+ printk(KERN_INFO"%s:Trying to reconfiure EP before previous finished\n", __FUNCTION__);
+ }
+#endif
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "urb: %x sent: %d last: %d", endpoint->tx_urb, endpoint->sent, endpoint->last);
+ tx_urb = pcd_tx_complete_irq(endpoint, 0);
+
+ UNLESS (tx_urb) {
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "START IN: %02x finished", endpoint->bEndpointAddress[0]);
+ UNLESS (epn) { // if not EP0 we are done
+ if (mxc_new_address != mxc_usb_address) {
+ mxc_usb_address = mxc_new_address;
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "START IN: SETTING ADDRESS %x", mxc_usb_address);
+ fs_wl(OTG_FUNC_DEV_ADDR, mxc_usb_address);
+ }
+ mxc_pcd_start_ep0_setup(endpoint); // if EP0 then restart for receive setup
+ }
+ return;
+ }
+
+ if (pcd_tx_sendzlp(endpoint)) { // check if we need to send ZLP
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "START IN: ZLP tx_urb: %x", tx_urb);
+ mxc_sendzlp(epn, endpoint, wMaxPacketSize); // ZLP
+ return;
+ }
+
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG6(mxc_pcd_instance->TAG, "START IN: %02x tx_urb: %x actual: %d sent: %d last: %d %s", endpoint->bEndpointAddress[0], tx_urb,
+ tx_urb->actual_length, endpoint->sent,
+ endpoint->last, (tx_urb->flags & USBD_URB_SENDZLP) ? "ZLP": "");
+
+
+ /* check if xfil/yfil bits are set, clear if necessary
+ */
+ if (epn && fs_rl(OTG_FUNC_XFILL_STAT) & ep_num) {
+ fs_wl(OTG_FUNC_XFILL_STAT, ep_num);
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "CLEAR XFILL: %08x", fs_rl(OTG_FUNC_XFILL_STAT));
+ }
+ if (epn && fs_rl(OTG_FUNC_YFILL_STAT) & ep_num) {
+ fs_wl(OTG_FUNC_YFILL_STAT, ep_num);
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "CLEAR YFILL: %08x", fs_rl(OTG_FUNC_YFILL_STAT));
+ }
+
+ /* reset ready status
+ */
+ // XXX mxc_reset_ep_rdy(ep_num);
+
+ /* Three cases
+ * 1. Complete transfer less than 2 buffers - non DMA
+ * 2. Multiples of 2 buffers - DMA
+ * 3. Remainder after sending all full sized buffers, same as 1, non DMA
+ */
+ remaining = tx_urb->actual_length - endpoint->sent;
+
+ if (remaining < (2*wMaxPacketSize)) {
+ endpoint->last = remaining;
+ if (TRACE_VERY_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "LAST: %d SHORT", endpoint->last);
+ }
+ else {
+ #ifdef NO_DMA_IN
+ endpoint->last = 2*wMaxPacketSize;
+ #else /* NO_DMA_IN */
+ endpoint->last = (remaining / (2*wMaxPacketSize)) * (2*wMaxPacketSize);
+ #endif /* NO_DMA_IN */
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "LAST: %d EVEN", endpoint->last);
+ }
+
+ #ifdef NO_DMA_IN
+ if (endpoint->last <= (2*wMaxPacketSize)) {
+
+ if (TRACE_VERBOSE) {
+ TRACE_MSG2(mxc_pcd_instance->TAG, "XCNT: %d memcpy: %d", endpoint->last,
+ MAX(32, (endpoint->last + 3) & ~3));
+ TRACE_NSEND(mxc_pcd_instance->TAG, endpoint->last, tx_urb->buffer + endpoint->sent);
+ }
+
+ /* N.b. - copies must be 32bit */
+ memcpy((void *)data_x_address(epn, USB_DIR_IN),
+ tx_urb->buffer + endpoint->sent,
+ MAX(32, (endpoint->last + 3) & ~3));
+ fs_wl(OTG_FUNC_XFILL_STAT, ep_num);
+ fs_wl(OTG_FUNC_YFILL_STAT, ep_num);
+ mxc_ep_config(epn, USB_DIR_IN, ep_num, wMaxPacketSize, wMaxPacketSize, USB_DIR_IN, endpoint->last, 0);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG6(mxc_pcd_instance->TAG,
+ "EP CONFIG NODMA: epn: %02x num: %02x ttlbtecnt: %d dir: %d XFILL: %08x YFILL: %08x",
+ epn, ep_num, endpoint->last, USB_DIR_IN,
+ fs_rl(OTG_FUNC_XFILL_STAT), fs_rl(OTG_FUNC_YFILL_STAT));
+ }
+ else {
+ #endif
+ /* configure endpoint first to setup buffersize correctly, we set it to
+ * packetsize or the actual transfer size IFF less than twice packetsize.
+ * Note that this requires sequential x/y buffer allocation.
+ */
+ CACHE_SYNC_TX (tx_urb->buffer + endpoint->sent, endpoint->last);
+
+ mxc_ep_config(epn, USB_DIR_IN, ep_num, wMaxPacketSize,
+ (endpoint->last > wMaxPacketSize) ? /* XXX 2*wMaxPacketSize ??? */
+ wMaxPacketSize : endpoint->last,
+ endpoint->bmAttributes[0] & 0x3,
+ endpoint->last, 0);
+
+ fs_wl(OTG_DMA_EPN_MSA(dma_num), virt_to_phys(tx_urb->buffer + endpoint->sent));
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_DMA_EP_EN, ep_num);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG5(mxc_pcd_instance->TAG, "EP CONFIG DMA: buf: %08x epn: %02x num: %02x ttlbtecnt: %d dir: %d",
+ (void *)virt_to_phys(tx_urb->buffer), epn, ep_num, endpoint->last, USB_DIR_IN);
+ #ifdef NO_DMA_IN
+ }
+ #endif
+#ifdef MXC_TRACK_TIMER
+ // setup a tracking timer
+ init_timer (&(endpoint->pcd_hr_timer));
+ endpoint->pcd_hr_timer.expires = jiffies + 1000;
+ endpoint->pcd_hr_timer.function = mxc_pcd_hrt_callback;
+ endpoint->pcd_hr_timer.data = (unsigned long) endpoint;
+ endpoint->pcd_hr_timer.arch_cycle_expires = 0;
+ add_timer(&endpoint->pcd_hr_timer);
+#endif
+ mxc_sof_active(ep_num);
+ mxc_set_ep_rdy(ep_num);
+}
+
+/*!
+ * mxc_pcd_stop_in() - process interrupt for transmitted buffer
+ * @param pcd - pcd instance
+ * @param epn -endpoint number
+ * @param endpoint - endpoint instance
+ */
+static void inline
+mxc_pcd_stop_in (struct pcd_instance *pcd, int epn, struct usbd_endpoint_instance *endpoint)
+{
+ u32 ep_num = ep_num_dir(epn, USB_DIR_IN);
+ //TRACE_MSG1(mxc_pcd_instance->TAG, "%02x", epn);
+ mxc_stop_ep(epn, USB_DIR_IN);
+ mxc_sof_cancel(ep_num);
+#ifdef MXC_TRACK_TIMER
+ del_timer(&endpoint->pcd_hr_timer);
+#endif
+
+ mxc_pcd_start_endpoint_in(pcd, endpoint);
+}
+/*!
+ * mxc_pcd_cancel_in_irq() - cancel IN urb
+ */
+static void inline
+mxc_pcd_cancel_in_irq (struct pcd_instance *pcd,struct usbd_urb *urb)
+{
+ struct usbd_endpoint_instance *endpoint = urb->endpoint;
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(mxc_pcd_instance->TAG, "urb: %x endpoint: %x epn: %d", urb, endpoint, epn);
+ mxc_stop_ep(epn, USB_DIR_IN);
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_stop_ep0_setup() - setup endpoint zero
+ * @param pcd - pcd instance
+ * @param endpoint - endpoint instance
+ */
+static void
+mxc_pcd_stop_ep0_setup (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ static struct usbd_device_request request;
+ //u8 *cp = (u8 *) &request;
+ u32 ep0 = fs_rl(ep_word(0, USB_DIR_OUT, 0));
+ u32 ep3 = fs_rl(ep_word(0, USB_DIR_OUT, 3));
+
+ //fs_wl_set(mxc_pcd_instance->TAG,OTG_FUNC_XYINT_STEN_CLR, ep_num_both(0));
+ fs_wl_set(mxc_pcd_instance->TAG,OTG_FUNC_IINT_CLR, ep_num_both(0));
+
+ UNLESS (ep0 & EP0_SETUP) { // check if SETUP flag set
+ mxc_pcd_stop_out(pcd, 0, endpoint);
+ return;
+ }
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "EP0 - SETUP: size: %x", ep3 & 0xffff);
+ pcd_tx_cancelled_irq(endpoint);
+ pcd_rcv_cancelled_irq(endpoint);
+
+ /*
+ * Ensure that toggle is set so that any data returned starts as DATA1
+ * XXX It might be more appropriate to do this in start_endpoint_in().
+ */
+ mxc_set_toggle_ep(0x2);
+ fs_memcpy((u8 *)&request, (u8 *)data_x_address(0, USB_DIR_OUT), 8);
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_XINT_STAT, ep_num_out(0));
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_XFILL_STAT, ep_num_out(0));
+
+ if (pcd_recv_setup_irq(pcd, &request)) { // process setup packet
+ if (TRACE_VERBOSE)
+ TRACE_MSG0(mxc_pcd_instance->TAG, "pcd_ep0: STALLING");
+ //fs_rl(OTG_FUNC_XINT_STAT);
+ //mxc_ep_config_nodma(0, USB_DIR_IN, 1, endpoint->wMaxPacketSize[0], EP_FORMAT_CONTROL, 0, 1, 1);
+ mxc_halt_endpoint0_in(1);
+ mxc_halt_endpoint0_in(0);
+ return;
+ }
+ if (request.wLength) {
+ // mxc_pcd_start_endpoint_in() will start xfer
+ //mxc_pcd_start_ep0_setup(endpoint); // restart endpoint
+ return;
+ }
+ if ((request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {
+ //RETURN_IF (request.wLength); // mxc_pcd_start_endpoint_in() will start xfer
+ mxc_sendzlp(0, endpoint, endpoint->wMaxPacketSize[0]); // wLength zero, so send ZLP
+ return;
+ }
+ mxc_pcd_start_ep0_setup(endpoint); // restart endpoint
+}
+/* ********************************************************************************************* */
+#ifdef MXC_ACTIVE
+static u32 mxc_pcd_missed;
+#endif /* MXC_ACTIVE */
+
+static u32 mxc_pcd_missing;
+static u32 mxc_pcd_fixed;
+
+/*!
+ * mxc_pcd_int_hndlr() - interrupt handle
+ * @return interrupt handled status
+ */
+irqreturn_t mxc_pcd_int_hndlr (void)
+{
+ struct pcd_instance *pcd = mxc_pcd_instance;
+ otg_tick_t ticks = (pcd->otg && pcd->otg->ocd_ops && pcd->otg->ocd_ops->ticks) ? pcd->otg->ocd_ops->ticks () : 0;
+ //u32 cmd_stat, sint_stat, ep_dstat, ep_stats, mask;
+ u32 cmd_stat, sint_stat, ep_dstat, ep_stats;
+ //u32 sint_sten = fs_rl(OTG_FUNC_SINT_STEN);
+
+ int i;
+ BOOL ep0_int = FALSE;
+ BOOL epn_int = FALSE;
+ BOOL other_int = FALSE;
+
+ if (!pcd)
+ return IRQ_HANDLED;
+
+
+ /* Reset, clear and process any System interrupts
+ */
+ if ((sint_stat = fs_rl(OTG_FUNC_SINT_STAT)))
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_SINT_STAT, sint_stat);
+
+ cmd_stat = fs_rl(OTG_FUNC_CMD_STAT);
+
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+
+ //if (TRACE_VERY_VERBOSE)
+ TRACE_MSG6(mxc_pcd_instance->TAG, "sint: %08x cmd: %08x EP_EN: %08x EP_RDY: %08x EP_DSTAT: %08x FIXED: %d",
+ sint_stat, cmd_stat, fs_rl(OTG_FUNC_EP_EN), fs_rl(OTG_FUNC_EP_RDY),
+ fs_rl(OTG_FUNC_EP_DSTAT), mxc_pcd_fixed);
+
+
+ if ((cmd_stat & COMMAND_RESETDET) || (sint_stat & SYSTEM_RESETINT)) {
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "RESETDET: %08x", sint_stat);
+ //printk(KERN_INFO"%s: RESETDET\n", __FUNCTION__);
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_RESET, 0);
+ //mxc_func_clock_on();
+ fs_wl(OTG_FUNC_DEV_ADDR, 0);
+ mxc_new_address = mxc_usb_address = 0;
+ fs_wl(OTG_DMA_EP_CH_CLR, 0xffffffff);
+
+ //fs_wl(OTG_FUNC_XYINT_STEN_CLR, 0xffffffff);
+ fs_wl(OTG_FUNC_IINT_CLR, 0xffffffff);
+ fs_wl(OTG_FUNC_EP_DEN_CLR, 0xffffffff); // XXX this fails,
+
+ mxc_reset_toggles();
+
+ //fs_wl_clr(mxc_pcd_instance->TAG, OTG_FUNC_EP_RDY, fs_rl(OTG_FUNC_EP_RDY));
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_EP_RDY_CLR, fs_rl(OTG_FUNC_EP_RDY));
+
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_EP_DSTAT, fs_rl(OTG_FUNC_EP_DSTAT));
+
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_XINT_STAT, fs_rl(OTG_FUNC_XINT_STAT));
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_YINT_STAT, fs_rl(OTG_FUNC_YINT_STAT));
+ if (pcd->bus)
+ mxc_pcd_start_ep0_setup(pcd->bus->endpoint_array);
+ other_int = TRUE;
+ }
+ if ((cmd_stat & COMMAND_RSMINPROG) || (sint_stat & SYSTEM_RSMFININT)) {
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "RSMINPRO: %08x", sint_stat);
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_SINT_STAT,SYSTEM_RSMFININT);
+ mxc_func_clock_on();
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_ACTIVITY, 0);
+ other_int = TRUE;
+ }
+ if ((cmd_stat & COMMAND_SUPDET) || (sint_stat & SYSTEM_SUSPDETINT)) {
+ //int timeout = 0;
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "SUSPENDDET: %08x", sint_stat);
+ fs_wl(OTG_FUNC_CMD_STAT, COMMAND_SUPDET); /* tell hardware to suspend */
+ fs_wl(OTG_FUNC_SINT_STAT,SYSTEM_SUSPDETINT);
+ mxc_func_clock_off();
+ pcd_bus_event_handler_irq (pcd->bus, DEVICE_BUS_INACTIVE, 0);
+ other_int = TRUE;
+ }
+
+ while ((ep_dstat = fs_rl(OTG_FUNC_EP_DSTAT))) {
+
+ ep_stats = (ep_dstat | fs_rl(OTG_FUNC_XINT_STAT) | fs_rl(OTG_FUNC_YINT_STAT));
+
+ /* clear the done status flags */
+ if (TRACE_VERBOSE)
+ TRACE_MSG1(mxc_pcd_instance->TAG, "EP STATS: %08x", ep_stats);
+ //printk (KERN_INFO"%s: EP STATS: %08x\n", __FUNCTION__, ep_stats);
+ fs_wl_set(mxc_pcd_instance->TAG, OTG_FUNC_EP_DSTAT, ep_dstat);
+
+ /* check ep0 first - special handling for control endpoint, need to check dstat, y/x stats */
+ if (ep_stats & EP_OUT) {
+ ep0_int = TRUE;
+ mxc_pcd_stop_ep0_setup(pcd, pcd->bus->endpoint_array);
+ }
+ if (ep_stats & EP_IN) {
+ ep0_int = TRUE;
+ mxc_pcd_stop_in(pcd, 0, pcd->bus->endpoint_array);
+ }
+
+ /* now check all of the data endpoints, only if in dstat */
+ for (i = 1, ep_dstat >>= 2; ep_dstat && (i < 16); i++, ep_dstat >>= 2) {
+ if (ep_dstat & EP_OUT) {
+ epn_int = TRUE;
+ mxc_pcd_stop_out(pcd, i, pcd->bus->endpoint_array + (2 * i));
+ }
+ if (ep_dstat & EP_IN) {
+ //epn_int = TRUE;
+ mxc_pcd_stop_in(pcd, i, pcd->bus->endpoint_array + (2 * i) + 1);
+ }
+ }
+ }
+
+ if (epn_int)
+ TRACE_ELAPSED(mxc_pcd_instance->TAG, "EPN 0. INT", ticks, 0);
+
+ return IRQ_HANDLED;
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_set_address() - set the USB address for this device
+ * @param pcd
+ * @param address
+ */
+static void inline
+mxc_pcd_set_address (struct pcd_instance *pcd, u8 address)
+{
+ mxc_usb_address = 0;
+ mxc_new_address = address; /* this will be used in the interrupt handler */
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_en_func() - enable
+ * This is called to enable / disable the PCD and USBD stack.
+ * @param otg - otg instance
+ * @param flag - SET or RESET
+ */
+static void
+mxc_pcd_en_func (struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ struct usbd_bus_instance *bus = pcd->bus;
+ u32 hwmode = fs_rl(OTG_CORE_HWMODE);
+ int epn;
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(otg->pcd->TAG, "PCD_EN: SET" );
+ otg_pthread_mutex_lock(&pcd->mutex);
+
+ fs_andl(OTG_CORE_HWMODE, ~MODULE_CRECFG_HOST); // set to FUNCTION hnp
+
+ TRACE_MSG1(otg->pcd->TAG, "PCD_EN: clr hwmode: %08x", fs_rl(OTG_CORE_HWMODE));
+
+ fs_orl(OTG_CORE_HWMODE, MODULE_CRECFG_FUNC); // set to software hnp
+
+ TRACE_MSG2(otg->pcd->TAG, "PCD_EN: set hwmode: %08x want %08x",
+ fs_rl(OTG_CORE_HWMODE),
+ MODULE_CRECFG_FUNC); // set to software hnp
+
+ mxc_main_clock_on();
+ mxc_func_clock_on();
+
+ if (pcd->bus){
+ mxc_pcd_start_ep0_setup(pcd->bus->endpoint_array);
+ for (epn = 1; epn < bus->endpoints; epn++)
+ mxc_pcd_setup_ep (pcd, epn, bus->endpoint_array + epn);
+ }
+ /* C.f. L3 12.1 - enable Slave, Band Gap enable, and Comparator enable.
+ * C.f. SCM-11 version of the USBOTG specification, ABBusReq enables the charge pump
+ */
+ fs_andl(OTG_CORE_HNP_CSTAT, ~(MODULE_MASTER | MODULE_SLAVE |
+ MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ));
+
+ fs_orl(OTG_CORE_HNP_CSTAT, MODULE_SLAVE | MODULE_CMPEN | MODULE_BGEN | MODULE_SWAUTORST | MODULE_ABBUSREQ);
+ //fs_rl(OTG_CORE_HNP_CSTAT);
+
+ fs_orl(OTG_FUNC_SINT_STEN, ( SYSTEM_DONEREGINT_EN |
+ SYSTEM_SUSPDETINT_EN | SYSTEM_RSMFININT_EN | SYSTEM_RESETINT_EN));
+
+ fs_orl(OTG_CORE_CINT_STEN, MODULE_FCINT_EN | MODULE_ASFCINT_EN);
+ otg->pcd->active = TRUE;
+ otg_pthread_mutex_lock(&pcd->mutex);
+
+ break;
+
+ case RESET:
+ TRACE_MSG1(otg->pcd->TAG, "PCD_EN: RESET hwmode: %08x", hwmode);
+
+ otg_pthread_mutex_lock(&pcd->mutex);
+ otg->pcd->active = FALSE;
+ mxc_func_clock_on();
+ fs_andl(OTG_FUNC_SINT_STEN, 0);
+ fs_andl(OTG_CORE_CINT_STEN, ~(MODULE_FCINT_EN | MODULE_ASFCINT_EN));
+
+ mxc_func_clock_off();
+
+ /* reset the function core
+ */
+ fs_wl_set(otg->pcd->TAG, OTG_CORE_RST_CTRL, MODULE_RSTFSIE | MODULE_RSTFC);
+ while (fs_rl(OTG_CORE_RST_CTRL));
+ fs_orl(OTG_CORE_HWMODE,MODULE_CRECFG_SHNP); // set to software hnp
+ otg_pthread_mutex_lock(&pcd->mutex);
+
+ pcd_bus_event_handler_irq (bus, DEVICE_RESET, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0);
+
+ break;
+ }
+ TRACE_MSG4(otg->pcd->TAG, "HWMODE: %08x CINT_STEN: %08x SINT_STEN: %08x HNP_CSTAT: %08x",
+ fs_rl(OTG_CORE_HWMODE), fs_rl(OTG_CORE_CINT_STEN),
+ fs_rl(OTG_FUNC_SINT_STEN), fs_rl(OTG_CORE_HNP_CSTAT) );
+}
+
+#if CONFIG_OTG_REMOTE_WAKEUP
+/*!
+ * mxc_remote_wakeup() - perform remote wakeup.
+ * Initiate a remote wakeup to the host.
+ * @param otg - otg instance
+ * @param flag - SET or RESET
+ */
+static void
+mxc_remote_wakeup(struct otg_instance *otg, u8 flag)
+{
+ switch(flag) {
+ case PULSE:
+ TRACE_MSG1(otg->pcd->TAG, "MX2_REMOTE_WAKEUP: OTG_FUNC_CMD_STAT: %08x ", fs_rl(OTG_FUNC_CMD_STAT) );
+ otg_pthread_mutex_lock(&otg->pcd->mutex);
+ mxc_func_clock_on();
+ fs_wl(OTG_FUNC_CMD_STAT, COMMAND_RSMINPROG);
+ otg_pthread_mutex_unlock(&otg->pcd->mutex);
+ TRACE_MSG1(otg->pcd->TAG, "MX2_REMOTE_WAKEUP: OTG_FUNC_CMD_STAT: %08x RESUMED", fs_rl(OTG_FUNC_CMD_STAT) );
+ default:
+ break;
+ }
+}
+#endif /* CONFIG_OTG_REMOTE_WAKEUP */
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pcd_start() - start the UDC
+ * @param pcd
+ */
+static void
+mxc_pcd_start (struct pcd_instance *pcd)
+{
+ //fs_rl(OTG_FUNC_SINT_STEN);
+ fs_wl(OTG_DMA_MISC_CTRL, OTG_DMA_MISC_ARBMODE); /* round-robin mode */
+}
+
+/*!
+ * mxc_pcd_stop() - stop the UDC
+ * @param pcd
+ */
+static void
+mxc_pcd_stop (struct pcd_instance *pcd)
+{
+ TRACE_MSG0(pcd->TAG, "UDC STOP");
+}
+
+/*!
+ * mxc_pcd_set_endpoints() - setup the physical endpoints for the endpoint map
+ * @param pcd
+ * @param endpointsRequested
+ * @param endpoint_map_array
+ */
+static int
+mxc_pcd_set_endpoints (struct pcd_instance *pcd, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array)
+{
+ TRACE_MSG0(pcd->TAG, "Enable system control interrupts");
+ #ifdef MX2_OTG_XCVR_DEVAD
+ fs_wl(OTG_SYS_CTRL, SYS_CTRL_I2C_WU_INT_EN | SYS_CTRL_OTG_WU_INT_EN | SYS_CTRL_HOST_WU_INT_EN |
+ SYS_CTRL_FNT_WU_INT_EN | 0x1);
+ #else /* MX2_OTG_XCVR_DEVAD */
+ fs_wl(OTG_SYS_CTRL, SYS_CTRL_OTG_WU_INT_EN | SYS_CTRL_HOST_WU_INT_EN | SYS_CTRL_FNT_WU_INT_EN | 0x1);
+ #endif /* MX2_OTG_XCVR_DEVAD */
+ return 0;
+}
+
+/*!
+ * mxc_pcd_framenum() - get current framenum
+ */
+static u16
+mxc_pcd_framenum (struct otg_instance *otg)
+{
+ return fs_rl(OTG_FUNC_FRM_NUM);
+}
+
+/*
+ * mxc_endpoint_halted() - is endpoint halted
+ */
+static int
+mxc_endpoint_halted (struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint)
+{
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ int dir = (endpoint->bEndpointAddress[0] & 0x80) ? 1 : 0;
+
+ TRACE_MSG2(pcd->TAG, "epn: %02x dir: %x", epn, dir);
+ return fs_rl(ep_word(epn, dir, 0)) & (1 << 31) ? TRUE : FALSE;
+}
+
+
+/* mxc_halt_endpoint - halt endpoint
+ */
+int mxc_halt_endpoint(struct pcd_instance *pcd, struct usbd_endpoint_instance *endpoint, int flag)
+{
+ int epn = endpoint->bEndpointAddress[0] & 0x7f;
+ int dir = (endpoint->bEndpointAddress[0] & 0x80) ? 1 : 0;
+ u32 ep_num = ep_num_dir(epn, dir);
+ u32 temp=~(1<<31);
+ //u32 toggle = fs_rl(OTG_FUNC_EP_TOGGLE);
+ u32 ep0_current, ep0_new;
+
+ /*
+ * Ensure that toggle is reset so that any data returned starts as DATA0
+ * XXX It might be more appropriate to do this in start_endpoint_in().
+ */
+ mxc_reset_toggle_ep(ep_num);
+ // XXX fs_wl(OTG_DMA_EP_CH_CLR, ep_num); // XXX causes problems
+
+ ep0_current = fs_rl(ep_word(epn, dir, 0));
+ ep0_new = ((flag ? 1 : 0) << 31) | (ep0_current & temp);
+
+ UNLESS (ep0_current == ep0_new) fs_wl(ep_word(epn, dir, 0), ep0_new);
+ return 0;
+}
+
+/* mxc_pcd_disable -
+ */
+void mxc_pcd_disable(struct pcd_instance *pcd)
+{
+ TRACE_MSG0(pcd->TAG, "--");
+}
+
+/* mxc_pcd_disable_ep -
+ */
+void mxc_pcd_disable_ep(struct pcd_instance *pcd, unsigned int ep, struct usbd_endpoint_instance * ep_instance)
+{
+ TRACE_MSG0(pcd->TAG, "--");
+}
+
+/* ********************************************************************************************* */
+
+#if defined(CONFIG_OTG_NOC99)
+struct usbd_pcd_ops usbd_pcd_ops;
+
+extern pcd_ops pcd_ops;
+
+#if 0
+#if defined(CONFIG_OTG_LNX) && defined(LINUX26)
+int mxc_pcd_mod_init_l26(void);
+void mxc_pcd_mod_exit_l26(void);
+struct pcd_ops pcd_ops = { mxc_pcd_mod_init_l26, mxc_pcd_mod_exit_l26, };
+#else /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+
+int mxc_pcd_mod_init(struct otg_instance *otg);
+void mxc_pcd_mod_exit(struct otg_instance *otg);
+struct pcd_ops pcd_ops = { mxc_pcd_mod_init, mxc_pcd_mod_exit, };
+#endif /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+#endif
+
+/*!
+ * mxc_pcd_global_init() - initialize globals
+ */
+void
+mxc_pcd_global_init(void)
+{
+ ZERO(usbd_pcd_ops);
+ usbd_pcd_ops.bmAttributes =
+ USB_ABMTTRIBUTE_RESERVED |
+ #ifdef CONFIG_OTG_SELF_POWERED
+ USB_BMATTRIBUTE_SELF_POWERED |
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ USB_BMATTRIBUTE_REMOTE_WAKEUP |
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED;
+ #ifndef CONFIG_OTG_SELF_POWERED
+ usbd_pcd_ops.bMaxPower = CONFIG_OTG_BMAXPOWER;
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ usbd_pcd_ops.max_endpoints = UDC_MAX_ENDPOINTS;
+ usbd_pcd_ops.ep0_packetsize = EP0_PACKETSIZE;
+ //usbd_pcd_ops.capabilities = REMOTE_WAKEUP_SUPPORTED;
+ usbd_pcd_ops.name = UDC_NAME;
+ usbd_pcd_ops.start = mxc_pcd_start;
+ usbd_pcd_ops.stop = mxc_pcd_stop;
+ usbd_pcd_ops.disable = mxc_pcd_disable;
+ usbd_pcd_ops.disable_ep = mxc_pcd_disable_ep;
+ usbd_pcd_ops.start_endpoint_in = mxc_pcd_start_endpoint_in;
+ usbd_pcd_ops.start_endpoint_out = mxc_pcd_start_endpoint_out;
+ usbd_pcd_ops.request_endpoints = pcd_request_endpoints;
+ usbd_pcd_ops.set_endpoints = mxc_pcd_set_endpoints;
+ usbd_pcd_ops.set_address = mxc_pcd_set_address;
+ usbd_pcd_ops.setup_ep = mxc_pcd_setup_ep;
+ usbd_pcd_ops.halt_endpoint = mxc_halt_endpoint;
+ usbd_pcd_ops.endpoint_halted = mxc_endpoint_halted;
+
+ usbd_pcd_ops.cancel_in_irq = mxc_pcd_cancel_in_irq;
+ usbd_pcd_ops.cancel_out_irq = mxc_pcd_cancel_out_irq;
+ usbd_pcd_ops.max_endpoints = 8;
+
+}
+/* ********************************************************************************************* */
+#else /* defined(CONFIG_OTG_NOC99) */
+struct usbd_pcd_ops usbd_pcd_ops;
+struct
+usbd_pcd_ops usbd_pcd_ops = {
+ .bmAttributes =
+ USB_BMATTRIBUTE_RESERVED
+ #ifdef CONFIG_OTG_SELF_POWERED
+ | USB_BMATTRIBUTE_SELF_POWERED
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ | USB_BMATTRIBUTE_REMOTE_WAKEUP
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ | USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED,
+ #ifndef CONFIG_OTG_SELF_POWERED
+ .bMaxPower = CONFIG_OTG_BMAXPOWER;
+ #endif /* CONFIG_OTG_SELF_POWERED */
+ .max_endpoints = UDC_MAX_ENDPOINTS,
+ .high_speed_capable = FALSE,
+ .ep0_packetsize = EP0_PACKETSIZE,
+ //.capabilities = REMOTE_WAKEUP_SUPPORTED,
+ .name = UDC_NAME,
+ .start = mxc_pcd_start,
+ .stop = mxc_pcd_stop,
+ .disable = mxc_pcd_disable,
+ .disable_ep = mxc_pcd_disable_ep,
+ .start_endpoint_in = mxc_pcd_start_endpoint_in,
+ .start_endpoint_out = mxc_pcd_start_endpoint_out,
+ .request_endpoints = pcd_request_endpoints,
+ .set_endpoints = mxc_pcd_set_endpoints,
+ .set_address = mxc_pcd_set_address,
+ .setup_ep = mxc_pcd_setup_ep,
+ .halt_endpoint = mxc_halt_endpoint,
+ .endpoint_halted = mxc_endpoint_halted,
+
+ .cancel_in_irq = mxc_pcd_cancel_in_irq,
+ .cancel_out_irq = mxc_pcd_cancel_out_irq,
+
+ .max_endpoints = 8,
+};
+
+#if 0
+#if defined(CONFIG_OTG_LNX) && defined(LINUX26)
+int mxc_pcd_mod_init_l26(struct otg_instance *otg);
+void mxc_pcd_mod_exit_l26(struct otg_instance *otg);
+struct pcd_ops pcd_ops = {
+ .mod_init = mxc_pcd_mod_init_l26, // called for module init
+#ifdef MODULE
+ .mod_exit = mxc_pcd_mod_exit_l26, // called for module exit
+#endif
+ .pcd_en_func = mxc_pcd_en_func,
+ .pcd_init_func = pcd_init_func,
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ .remote_wakeup_func = mxc_remote_wakeup,
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ .framenum = mxc_pcd_framenum,
+};
+
+#else /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+int mxc_pcd_mod_init(struct otg_instance *otg);
+void mxc_pcd_mod_exit(struct otg_instance *otg);
+struct pcd_ops pcd_ops = {
+ .mod_init = mxc_pcd_mod_init, // called for module init
+ .mod_exit = mxc_pcd_mod_exit, // called for module exit
+ .pcd_en_func = mxc_pcd_en_func,
+ .pcd_init_func = pcd_init_func,
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ .remote_wakeup_func = mxc_remote_wakeup,
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ .framenum = mxc_pcd_framenum,
+};
+#endif /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+#endif
+
+
+#endif /* defined(CONFIG_OTG_NOC99) */
+
+int mxc_pcd_mod_init_l26(struct otg_instance *otg);
+void mxc_pcd_mod_exit_l26(struct otg_instance *otg);
+
+/*!
+ * mxc_pcd_ops_init() - initialize globals
+ */
+void
+mxc_pcd_ops_init(void)
+{
+ ZERO(pcd_ops);
+ pcd_ops.pcd_en_func = mxc_pcd_en_func;
+ pcd_ops.pcd_init_func = pcd_init_func;
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ pcd_ops.remote_wakeup_func = mxc_remote_wakeup;
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ pcd_ops.framenum = mxc_pcd_framenum;
+ #if defined(CONFIG_OTG_LNX) && defined(LINUX26)
+ pcd_ops.mod_init = mxc_pcd_mod_init_l26; // called for module init
+ #ifdef MODULE
+ pcd_ops.mod_exit = mxc_pcd_mod_exit_l26; // called for module exit
+ #endif
+
+ #else /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+ pcd_ops.mod_init = mxc_pcd_mod_init; // called for module init
+ #ifdef MODULE
+ pcd_ops.mod_exit = mxc_pcd_mod_exit; // called for module exit
+ #endif
+ #endif /* defined(CONFIG_OTG_LNX) && defined(LINUX26) */
+}
+
+/* ********************************************************************************************* */
+
+/*! default initialization
+ */
+
+int pcd_mod_init (struct otg_instance *otg);
+void pcd_mod_exit (struct otg_instance *otg);
+
+int mxc_pcd_mod_init(struct otg_instance *otg)
+{
+ u32 hwmode;
+
+ mxc_pcd_instance = otg->pcd;
+ TRACE_MSG0(otg->pcd->TAG, "--");
+ pcd_mod_init(otg);
+
+ mxc_main_clock_on();
+ #if 0
+ fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_EP_BASE), (NUM_ETDS*16/4));
+ fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_EP_BASE), 0x200/4);
+ fs_clear_words((volatile u32 *)IO_ADDRESS(OTG_DATA_BASE), 0x400/4);
+ #endif
+
+
+ hwmode = fs_rl(OTG_CORE_HWMODE);
+ mxc_funcrev = hwmode >> 24;
+
+ TRACE_MSG1(otg->pcd->TAG, "funcrev: %04x", mxc_funcrev);
+
+ mxc_main_clock_off();
+
+ return 0;
+}
+
+void mxc_pcd_mod_exit(struct otg_instance *otg)
+{
+ int i;
+
+
+ TRACE_MSG0(otg->pcd->TAG, "--");
+ otg_pthread_mutex_lock(&otg->pcd->mutex);
+ for (i = 0; i < 16; i++) {
+ mxc_stop_ep(i, USB_DIR_IN);
+ mxc_stop_ep(i, USB_DIR_OUT);
+ }
+ otg_pthread_mutex_unlock(&otg->pcd->mutex);
+ pcd_mod_exit(otg);
+ mxc_pcd_instance = NULL;
+}
+
+/* ********************************************************************************************* */
+
+
+
+/*! New style linux 2.6 initialization.
+ */
+
+#if defined(LINUX26)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+static void mxc_pcd_release(struct platform_device *dev)
+#else
+static void mxc_pcd_release(struct device *dev)
+#endif
+{
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+}
+
+static int __init_or_module
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+mxc_pcd_remove(struct platform_device *dev)
+#else
+mxc_pcd_remove(struct device *dev)
+#endif
+{
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+ //mxc_pcd_mod_exit_l24(otg);
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "ok");
+ return 0;
+}
+
+
+static int __init
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+mxc_pcd_probe(struct platform_device *dev)
+#else
+mxc_pcd_probe(struct device *dev)
+#endif
+{
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ #else
+ dev->release = mxc_pcd_release;
+ #endif
+ //mxc_pcd_mod_init_l24(otg);
+ return 0;
+}
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+mxc_pcd_suspend(struct platform_device *dev, pm_message_t state)
+#else
+mxc_pcd_suspend(struct device *dev, u32 state, u32 phase)
+#endif
+{
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+ return 0;
+}
+
+static int
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
+mxc_pcd_resume(struct platform_device *dev)
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+mxc_pcd_resume(struct platform_device *dev, pm_message_t state)
+#else
+mxc_pcd_resume(struct device *dev, u32 phase)
+#endif
+{
+ //TRACE_MSG0(mxc_pcd_instance->TAG, "--");
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+static struct platform_driver mxc_pcd_driver = {
+ .driver = {
+ .name = "mxc-pcd",
+ },
+#else
+static struct device_driver mxc_pcd_driver = {
+
+ .name = "mxc-pcd",
+ .bus = &platform_bus_type,
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+
+ .probe = mxc_pcd_probe,
+ .remove = mxc_pcd_remove,
+
+ .suspend = mxc_pcd_suspend,
+ .resume = mxc_pcd_resume,
+
+};
+/* } FORMATTING */
+
+static struct platform_device mxc_pcd_platform = {
+ .name = "mxc-pcd",
+ .id = 1,
+};
+
+
+int mxc_pcd_mod_init_l26(struct otg_instance *otg)
+{
+ u32 hwmode;
+ int driver_registered = 0;
+ int platform_registered = 0;
+
+ //mxc_pcd_instance = otg->pcd;
+ TRACE_MSG0(otg->pcd->TAG, "--");
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ THROW_IF((driver_registered = platform_driver_register(&mxc_pcd_driver)), error);
+ #else
+ THROW_IF((driver_registered = driver_register(&mxc_pcd_driver)), error);
+ #endif
+ THROW_IF((platform_registered = platform_registered = platform_device_register(&mxc_pcd_platform)), error);
+
+ mxc_pcd_mod_init(otg);
+
+ CATCH(error) {
+ if (platform_registered) platform_device_unregister(&mxc_pcd_platform);
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ if (driver_registered) platform_driver_unregister(&mxc_pcd_driver);
+ #else
+ if (driver_registered) driver_unregister(&mxc_pcd_driver);
+ #endif
+ }
+
+ return 0;
+}
+
+void mxc_pcd_mod_exit_l26(struct otg_instance *otg)
+{
+ TRACE_MSG0(otg->pcd->TAG, "--");
+ mxc_pcd_mod_exit(otg);
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ platform_driver_unregister(&mxc_pcd_driver);
+ #else
+ driver_unregister(&mxc_pcd_driver);
+ #endif
+ platform_device_unregister(&mxc_pcd_platform);
+ //otg->pcd->TAG = 0;
+ //mxc_pcd_instance = NULL;
+}
+
+
+#endif /* defined(LINUX26) */
+
+/* ********************************************************************************************* */
diff --git a/drivers/otg/hardware/mxc-pmic.c b/drivers/otg/hardware/mxc-pmic.c
new file mode 100644
index 000000000000..1b214d7969ea
--- /dev/null
+++ b/drivers/otg/hardware/mxc-pmic.c
@@ -0,0 +1,1111 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mc13783.c -- Freescale mc13783 Connectiviey Transceiver Controller driver
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/mxc/mxc-pmic.c|20070710021518|30741
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>,
+ * Shahrad Payandeh <sp@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/mxc-pmic.c
+ * @brief mc13783 Transciever Controller Driver
+ *
+ * This is a simple transceiver driver for the mc13783 transceiver
+ * using the Freescale mc13783 connectivity driver.
+ *
+ * @ingroup FSOTG
+ *
+ *
+ */
+
+#include <asm/delay.h>
+
+#include <otg/pcd-include.h>
+#include <asm/arch/gpio.h>
+
+
+#include <asm/arch/pmic_external.h>
+#include <asm/arch/pmic_convity.h>
+
+
+
+//WORK_ITEM pmic_work_bh;
+//WORK_ITEM pmic_otg_wq;
+
+struct otg_task *pmic_work_task;
+struct otg_task *pmic_otg_task;
+
+int global_flag = 0;
+int global_flag_array[20], start_flag, end_flag;
+int det_dm_hi, det_dp_hi;
+
+struct otg_task *pmic_task;
+
+#define PUDP_FLAG_SET 1
+#define PUDP_FLAG_RESET 2
+#define UPD_FLAG_SET 4
+#define UPD_FLAG_RESET 8
+#define UDM_FLAG_SET 16
+#define UDM_FLAG_RESET 32
+#define DRV_VBUS_SET 64
+#define DRV_VBUS_RESET 128
+#define PUDM_FLAG_SET 256
+#define PUDM_FLAG_RESET 512
+#define DISCHRG_VBUS_SET 1024
+#define DISCHRG_VBUS_RESET 2048
+#define CHRG_VBUS_SET 4096
+#define CHRG_VBUS_RESET 8192
+
+//#define VBUS_TIMER
+
+PMIC_CONVITY_HANDLE pmic_handle = (PMIC_CONVITY_HANDLE) NULL;
+
+/*! pmic_bh- work task bottom handler
+ * @param data - otg_instance type pointer
+ *
+ */
+void pmic_bh(void *data)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+
+ do {
+
+// printk(KERN_INFO"%s: AAAA\n", __FUNCTION__);
+ TRACE_MSG0(REMOVE_TCD, "--");
+ global_flag = global_flag_array[start_flag];
+ if (global_flag & PUDP_FLAG_SET) { //set DP pullup
+ pmic_convity_usb_set_speed(pmic_handle, USB_FULL_SPEED);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_PU);
+ }
+ if (global_flag & PUDP_FLAG_RESET) { //reset DP pullup
+ pmic_convity_usb_set_speed(pmic_handle, USB_FULL_SPEED);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+ }
+ if (global_flag & PUDM_FLAG_SET) { //set DM pullup
+ pmic_convity_usb_set_speed(pmic_handle, USB_LOW_SPEED);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_PU);
+ }
+ if (global_flag & PUDM_FLAG_RESET) { //reset DM pullup
+ pmic_convity_usb_set_speed(pmic_handle, USB_LOW_SPEED);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+ }
+ if (global_flag & UPD_FLAG_SET) { //set DP pulldown
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_UDP_PD); //DP pull down switch is on
+ }
+ if (global_flag & UPD_FLAG_RESET) { //reset DP pulldown
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD); //DP pull down switch is off
+ }
+ if (global_flag & UDM_FLAG_SET) { //set DM pulldown
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_UDM_PD); //DP pull down switch is on
+ }
+ if (global_flag & UDM_FLAG_RESET) { //reset DM pulldown
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD); //DP pull down switch is off
+ }
+ if (global_flag & DRV_VBUS_SET) { //enable vbus voltage
+ pmic_convity_set_output(pmic_handle, TRUE, TRUE); //enable VBUS
+ }
+ if (global_flag & DRV_VBUS_RESET) { //disable vbus voltage
+ pmic_convity_set_output(pmic_handle, TRUE, FALSE); //disable VBUS
+ }
+ if (global_flag & CHRG_VBUS_SET) { //enable vbus
+ pmic_convity_usb_otg_set_config(pmic_handle,
+ USB_VBUS_CURRENT_LIMIT_LOW_30MS);
+ }
+ if (global_flag & CHRG_VBUS_RESET) { //disable vbus
+ pmic_convity_set_output(pmic_handle, TRUE, FALSE); //disable VBUS
+ }
+ if (global_flag & DISCHRG_VBUS_SET) { //discharge vbus
+ pmic_convity_set_output(pmic_handle, TRUE, FALSE); //disable VBUS
+ pmic_convity_usb_otg_clear_config(pmic_handle,
+ USB_VBUS_PULLDOWN);
+ pmic_convity_usb_set_power_source(pmic_handle,
+ USB_POWER_INTERNAL,
+ USB_POWER_3V3);
+ pmic_convity_set_output(pmic_handle, FALSE, TRUE); //enable VUSB
+ }
+ if (global_flag & DISCHRG_VBUS_RESET) { //discharge vbus disable
+ pmic_convity_set_output(pmic_handle, TRUE, FALSE); //disable VBUS
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_VBUS_PULLDOWN);
+ pmic_convity_set_output(pmic_handle, FALSE, TRUE); //enable VUSB
+ }
+#if 1
+ TRACE_MSG3(REMOVE_TCD, "gloabl flag %d start_flag %d end_flag %d", global_flag,
+ start_flag, end_flag);
+#endif
+#if 0
+ printk(KERN_INFO "%d %d %d %d %d %d %d %d\n",
+ (global_flag & PU_FLAG_SET), (global_flag & PU_FLAG_RESET),
+ (global_flag & UPD_FLAG_SET), (global_flag & UPD_FLAG_RESET),
+ (global_flag & UDM_FLAG_SET), (global_flag & UDM_FLAG_RESET),
+ (global_flag & VBUSPDENB_RESET),
+ (global_flag & VBUSREGEN_RESET));
+#endif
+ global_flag = 0;
+ global_flag_array[start_flag] = 0;
+ if (start_flag++ > 15)
+ start_flag = 0;
+ //if (start_flag != end_flag)
+ // SCHEDULE_WORK(pmic_work_bh);
+
+// printk(KERN_INFO"%s: BBBB start: %d end: %d\n", __FUNCTION__, start_flag, end_flag);
+ } while (start_flag != end_flag);
+
+// printk(KERN_INFO"%s: CCCC\n", __FUNCTION__);
+}
+/*! pmic_work_proc - pmic tcd task roution
+ * @param data
+ */
+void *pmic_work_proc(otg_task_arg_t data)
+{
+ pmic_bh(data);
+ return NULL;
+}
+
+/*! pmic_bh_wakeup - wakeup the pmic bottom half
+ * */
+void pmic_bh_wakeup(void)
+{
+ TRACE_MSG0(REMOVE_TCD, "--");
+ //SCHEDULE_WORK(pmic_work_bh);
+ otg_up_work(pmic_work_task);
+}
+
+/* ********************************************************************************************** */
+/*! mxc_pmic_vbus - Do we have Vbus (cable attached?)
+ * Return non-zero if Vbus is detected.
+ * @param otg - otg instance pointer
+ */
+int mxc_pmic_vbus(struct otg_instance *otg)
+{
+#if 1
+ t_sensor_bits sense_bits;
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return 0;
+ }
+ return sense_bits.sense_usb2v0s;
+#else
+ return pmic_check_sense(sense_usb2v0s);
+#endif
+}
+/*! pmic_otg_event_bh_old -
+ * @param arg
+ */
+void pmic_otg_event_bh_old(void *arg)
+{
+ otg_event_set_irq(REMOVE_tcd_instance->otg, 1,
+ mxc_pmic_vbus(REMOVE_tcd_instance->otg), B_SESS_VLD, REMOVE_TCD,
+ "B_SESS_VLD");
+}
+
+/*! pmic_otg_event_bh - pmic otg event handler
+ * @param data - otg instance
+ */
+void pmic_otg_event_bh(void *data)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+ otg_current_t inputs;
+ t_sensor_bits sense_bits;
+ static BOOL force = TRUE;
+ static otg_current_t inputs_saved = 0;
+
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return;
+ }
+ TRACE_MSG6(REMOVE_TCD,
+ "usb4v4s%c usb2v0s%c usb0v8s:%c id_gnds%c id_floats%c id_se1s%c",
+ sense_bits.sense_usb4v4s ? ' ' : '/',
+ sense_bits.sense_usb2v0s ? ' ' : '/',
+ sense_bits.sense_usb0v8s ? ' ' : '/',
+ sense_bits.sense_id_gnds ? ' ' : '/',
+ sense_bits.sense_id_floats ? ' ' : '/',
+ sense_bits.sense_se1s ? ' ' : '/');
+
+ inputs = (sense_bits.sense_usb4v4s ? VBUS_VLD : VBUS_VLD_) |
+ (sense_bits.
+ sense_usb2v0s ? (B_SESS_VLD | A_SESS_VLD) : (B_SESS_VLD_ |
+ A_SESS_VLD_)) |
+ (sense_bits.sense_usb0v8s ? B_SESS_END_ : B_SESS_END) | (sense_bits.
+ sense_id_gnds
+ ? ID_GND :
+ ID_GND_) |
+ (sense_bits.sense_id_floats ? ID_FLOAT : ID_FLOAT_) | (sense_bits.
+ sense_se1s ?
+ SE1_DET :
+ SE1_DET_) |
+ (det_dp_hi ? DP_HIGH : DP_HIGH_) | (det_dm_hi ? DM_HIGH : DM_HIGH_);
+
+ // printk(KERN_INFO" inputs: %8X\n", inputs);
+ TRACE_MSG4(REMOVE_TCD,
+ "MC13783 EVENT: sense_bits: %8x otg inputs: %8x saved: %x diff: %x",
+ sense_bits.sense_se1s, inputs, inputs_saved,
+ inputs ^ inputs_saved);
+
+ RETURN_UNLESS(force || (inputs ^ inputs_saved));
+
+ inputs_saved = inputs;
+ otg_event(REMOVE_tcd_instance->otg, inputs, REMOVE_TCD, "PMIC OTG EVENT");
+
+ // gpio_config_int_en(2, 17, TRUE);
+ // gpio_config_int_en(2, 16, TRUE);
+
+ // gpio_clear_int (2, 17);
+ // gpio_clear_int (2, 16);
+
+}
+/*! pmic_otg_proc - called to porcess pmic otg event
+ * @param data - otg instance type pointer
+ */
+void *pmic_otg_proc(otg_task_arg_t data)
+{
+ pmic_otg_event_bh(data);
+ return NULL;
+}
+
+/*! pmic_otg_wakeup - called to wake up pmic_otg_task
+ */
+void pmic_otg_wakeup(void)
+{
+ TRACE_MSG0(REMOVE_TCD, "start");
+ //SCHEDULE_WORK(pmic_otg_wq);
+ otg_up_work(pmic_otg_task);
+ TRACE_MSG0(REMOVE_TCD, "finsih");
+}
+
+/*! gpio_c17_int_hndlr - interrupt handler
+ * @param irq - interrupt number
+ * @param dev_id - interrupt device ip
+ * @param regs - cpu registers snapshot
+ * @return interrput process result
+ */
+static irqreturn_t gpio_c17_int_hndlr(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ udelay(100);
+ if (gpio_get_data(2, 17) == 1) {
+ det_dm_hi = 1;
+ // mc13783_otg_wakeup ();
+ // gpio_config_int_en(2, 16, FALSE);
+ } else {
+ det_dm_hi = 0;
+ // gpio_config_int_en(2, 17, TRUE);
+ }
+ TRACE_MSG1(REMOVE_TCD, "Changing the state of DM to %d", det_dm_hi);
+ pmic_otg_wakeup();
+ return IRQ_HANDLED;
+}
+/*! gpio_c16_int_hndlr - interrupt handler
+ * @param irq - interrupt number
+ * @param dev_id - interrupt device ip
+ * @param regs - cpu registers snapshot
+ * @return interrput process result
+ */
+
+static irqreturn_t gpio_c16_int_hndlr(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ udelay(100);
+ if (gpio_get_data(2, 16) == 1) {
+ det_dp_hi = 1;
+ // mc13783_otg_wakeup ();
+ // gpio_config_int_en(2, 17, FALSE);
+ } else {
+ det_dp_hi = 0;
+ // gpio_config_int_en(2, 16, TRUE);
+ }
+ TRACE_MSG1(REMOVE_TCD, "Changing the state of DP to %d", det_dp_hi);
+ pmic_otg_wakeup();
+ return IRQ_HANDLED;
+}
+
+/*! mxc_pmic_id - Do we have Vbus (cable attached?)
+ * Return non-zero if Vbus is detected.
+ *
+ * @param otg - otg instance
+ */
+int mxc_pmic_id(struct otg_instance *otg)
+{
+ struct tcd_instance *tcd = otg->tcd;
+#if 1
+ t_sensor_bits sense_bits;
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return 0;
+ }
+ return sense_bits.sense_id_gnds;
+#else
+ return pmic_check_sense(sense_id_gnds);
+#endif
+}
+
+/* ********************************************************************************************* */
+/*! mxc_pmic_tcd_en() - used to enable/ disable mc13783
+ *
+ * @param otg - otg instance
+ * @param flag - enable/ disable flag
+ */
+void mxc_pmic_tcd_en(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ switch (flag) {
+ case SET:
+ case PULSE:
+ TRACE_MSG0(tcd->TAG, "SET/PULSE");
+ pmic_otg_wakeup();
+ // otg_event_set_irq(tcd_instance->otg, 1, mxc_mc13783_vbus(tcd_instance->otg), B_SESS_VLD, TCD, "B_SESS_VLD");
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "RESET");
+ break;
+ }
+}
+
+/* ********************************************************************************************* */
+/*! mxc_pmic_tcd_init() - used to enable mc13783
+ * @param otg - otg instance pointer
+ * @param flag -
+ *
+ */
+void mxc_pmic_tcd_init(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ switch (flag) {
+ case SET:
+ case PULSE:
+ TRACE_MSG0(tcd->TAG, "SET/PULSE");
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "RESET");
+ break;
+ }
+ pmic_otg_wakeup();
+ otg_event (otg, OCD_OK, otg->tcd->TAG, "MC13783 OK");
+}
+
+/*! mxc_pmic_chrg_vbus - used to enable or disable B-device Vbus charging
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_chrg_vbus(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "CHRG_VBUS_SET");
+ global_flag_array[end_flag] = CHRG_VBUS_SET;
+ pmic_bh_wakeup();
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "CHRG_VBUS_RESET");
+ global_flag_array[end_flag] = CHRG_VBUS_RESET;
+ pmic_bh_wakeup();
+ break;
+ case PULSE:
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+
+}
+
+/*! mxc_pmic_drv_vbus - used to enable or disable A-device driving Vbus
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_drv_vbus(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "DRV_VBUS_SET");
+ global_flag_array[end_flag] = DRV_VBUS_SET;
+ pmic_bh_wakeup();
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "DRV_VBUS_RESET");
+ global_flag_array[end_flag] = DRV_VBUS_RESET;
+ pmic_bh_wakeup();
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+
+}
+
+/*! mxc_pmic_dischrg_vbus - used to enable Vbus discharge
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_dischrg_vbus(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "DISCHRG VBUS SET");
+ global_flag_array[end_flag] = DISCHRG_VBUS_SET;
+ pmic_bh_wakeup();
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "DISCHRG VBUS RESET");
+ global_flag_array[end_flag] = DISCHRG_VBUS_RESET;
+ pmic_bh_wakeup();
+ break;
+ }
+}
+
+/*! mxc_pmic_mx21_vbus_drain - used to enable Vbus discharge
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_SET");
+ break;
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "OUTPUT: TCD_DISCHRG_VBUS_RESET");
+ break;
+ }
+}
+
+/*! mxc_pmic_dp_pullup_func - used to enable or disable peripheral connecting to bus
+ *
+ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5
+ *
+ * host peripheral
+ * d+ pull-up clr set
+ * d+ pull-down set clr
+ *
+ * d- pull-up clr clr
+ * d- pull-down set set
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+
+ */
+void mxc_pmic_dp_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN SET - Set DP PULLUP");
+ // global_flag |= PU_FLAG_SET;
+ global_flag_array[end_flag] = PUDP_FLAG_SET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_PU, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN RESET - Clr DP PULLUP");
+ // global_flag |= PU_FLAG_RESET;
+ global_flag_array[end_flag] = PUDP_FLAG_RESET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_PU, FALSE);
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+}
+
+/*! mxc_pmic_dm_pullup_func - used to enable or disable peripheral connecting to bus
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+
+ */
+void mxc_pmic_dm_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN SET - Set DM PULLUP");
+ // global_flag |= PU_FLAG_SET;
+ global_flag_array[end_flag] = PUDM_FLAG_SET;
+ pmic_bh_wakeup();
+ //mc13783_convity_set_pull_down_switch(PD_UDB_15, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN RESET - Clr DM PULLUP");
+ // global_flag |= PU_FLAG_RESET;
+ global_flag_array[end_flag] = PUDM_FLAG_RESET;
+ pmic_bh_wakeup();
+ //mc13783_convity_set_pull_down_switch(PD_UDB_15, FALSE);
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+
+}
+
+/*! mxc_pmic_dp_pulldown_func - used to enable or disable peripheral connecting to bus
+ *
+ * C.f. 5.1.6, 5.1.7, 5.2.4 and 5.2.5
+ *
+ * host peripheral
+ * d+ pull-up clr set
+ * d+ pull-down set clr
+ *
+ * d- pull-up clr clr
+ * d- pull-down set set
+ *
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_dp_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN SET - Set DP PULLDOWN");
+ // global_flag |= UPD_FLAG_SET;
+ global_flag_array[end_flag] = UPD_FLAG_SET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_UPD_15, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN RESET - Clr DP PULLDOWN");
+ // global_flag |= UPD_FLAG_RESET;
+ global_flag_array[end_flag] = UPD_FLAG_RESET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_UPD_15, TRUE);
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+
+}
+
+/*! mxc_pmic_dm_pulldown_func - used to enable or disable peripheral connecting to bus
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_dm_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN SET - Set DM PULLDOWN");
+ // global_flag |= UDM_FLAG_SET;
+ global_flag_array[end_flag] = UDM_FLAG_SET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_UDM_15, TRUE);
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "MC13783_LOC_CONN RESET - Clr DM PULLDOWN");
+ // global_flag |= UDM_FLAG_RESET;
+ global_flag_array[end_flag] = UDM_FLAG_RESET;
+ pmic_bh_wakeup();
+ // mc13783_convity_set_pull_down_switch(PD_UDM_15, TRUE);
+ break;
+ }
+ if (end_flag++ > 15)
+ end_flag = 0;
+
+}
+
+/*! mxc_pmic_peripheral_host_func - used to enable or disable peripheral connecting to bus
+ *
+ * A-Device D+ pulldown D- pulldown
+ * idle set set
+ * host set set
+ * peripheral reset set
+ *
+ * B-Device
+ * idle set set
+ * host set set
+ * peripheral reset set
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_peripheral_host_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET: // peripheral
+ TRACE_MSG0(tcd->TAG, "SET - CLR DP PULLDOWN");
+ break;
+
+ case RESET: // host
+ TRACE_MSG0(tcd->TAG, "RESET - SET DM PULLDOWN");
+ break;
+ }
+}
+
+/*! mxc_pmic_dm_det_func - used to enable or disable D- detect
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+
+ */
+void mxc_pmic_dm_det_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting DM_HI detect");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting DM_HI detect");
+ break;
+ }
+}
+
+/*! mxc_pmic_dp_det_func - used to enable or disable D+ detect
+ *
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ */
+void mxc_pmic_dp_det_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting DP_HI detect");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting DP_HI detect");
+ break;
+ }
+}
+
+/*! mxc_pmic_cr_det_func - used to enable or disable D+ detect
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_cr_det_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting CR_INT detect");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting CR_INT detect");
+ break;
+ }
+}
+
+/*! mxc_pmic_bdis_acon_func - used to enable or disable auto a-connect
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_bdis_acon_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting BDIS ACON");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting BDIS ACON");
+ break;
+ }
+}
+
+/*! mxc_pmic_id_pulldown_func - used to enable or disable ID pulldown
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_id_pulldown_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting ID PULLDOWN");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting ID PULLDOWN");
+ break;
+ }
+}
+
+/*! mxc_pmic_audio_func - used to enable or disable Carkit Interrupt
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_audio_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "SET AUDIO_EN");
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "RESET AUDIO_EN");
+ break;
+ }
+}
+
+/*! mxc_pmic_uart_func - used to enable or disable transparent uart mode
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_uart_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting UART_EN");
+ /* XXX enable uart */
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting UART_EN");
+ /* XXX disable uart */
+ break;
+ }
+}
+
+/*! mxc_pmic_mono_func - used to enable or disable mono audio connection
+ * @param otg - otg instance
+ * @param flag - enable/disable flag
+ *
+ */
+void mxc_pmic_mono_func(struct otg_instance *otg, u8 flag)
+{
+ struct tcd_instance *tcd = (struct tcd_instance *)otg->tcd;
+ //TRACE_MSG0(tcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(tcd->TAG, "setting MONO");
+ /* XXX enable mono output */
+ break;
+
+ case RESET:
+ TRACE_MSG0(tcd->TAG, "reseting MONO");
+ /* XXX disable mono output */
+ break;
+ }
+}
+
+/* ********************************************************************************************* */
+/*!
+ * mxc_pmic_usbi_handler() - event handler
+ *
+ *
+ *
+ */
+void pmic_usbi_handler(void)
+{
+
+ TRACE_MSG0(REMOVE_TCD, "--");
+ pmic_otg_wakeup();
+
+#if 0
+#if 1
+ t_sensor_bits sense_bits;
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return;
+ }
+
+ TRACE_MSG3(REMOVE_TCD, "MC13783 EVENT: 4V4S: %d 2V0S: %d 0V8S: %d",
+ sense_bits.sense_usb4v4s,
+ sense_bits.sense_usb2v0s, sense_bits.sense_usb0v8s);
+
+ otg_event(tcd_instance->otg,
+ (sense_bits.sense_usb4v4s ? VBUS_VLD : VBUS_VLD_) |
+ (sense_bits.
+ sense_usb2v0s ? (B_SESS_VLD | A_SESS_VLD) : (B_SESS_VLD_ |
+ A_SESS_VLD_)) |
+ (sense_bits.sense_usb0v8s ? B_SESS_END : B_SESS_END_), REMOVE_TCD,
+ "MC13783 USBI");
+#else
+ otg_event(tcd_instance->otg,
+ (pmic_check_sense(sense_usb4v4s) ? VBUS_VLD : VBUS_VLD_) |
+ (pmic_check_sense(sense_usb4v4s)
+ ? (B_SESS_VLD | A_SESS_VLD) : (B_SESS_VLD_ | A_SESS_VLD_)) |
+ (pmic_check_sense(sense_usb0v8s) ? B_SESS_END :
+ B_SESS_END_), REMOVE_TCD, "MC13783 USBI");
+#endif
+#endif
+}
+
+void pmic_idi_handler(void)
+{
+
+ TRACE_MSG0(REMOVE_TCD, "--");
+ pmic_otg_wakeup();
+
+#if 0
+#if 1
+ t_sensor_bits sense_bits;
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return;
+ }
+
+ TRACE_MSG2(REMOVE_TCD, "MC13783 EVENT: IDGNDS: %d IDFLOATS: %d",
+ sense_bits.sense_id_gnds, sense_bits.sense_id_floats);
+
+ otg_event(tcd_instance->otg,
+ (sense_bits.sense_id_gnds ? ID_GND : ID_GND_) |
+ (sense_bits.sense_id_floats ? ID_FLOAT : ID_FLOAT_),
+ REMOVE_TCD, "MC13783 IDI");
+#else
+ otg_event(tcd_instance->otg,
+ (pmic_check_sense(sense_id_gnds) ? ID_GND : ID_GND_) |
+ (pmic_check_sense(sense_id_floats) ? ID_FLOAT : ID_FLOAT_),
+ REMOVE_TCD, "MC13783 IDI");
+#endif
+#endif
+}
+
+void pmic_se1i_handler(void)
+{
+
+ TRACE_MSG0(REMOVE_TCD, "--");
+ pmic_otg_wakeup();
+
+#if 0
+#if 1
+ t_sensor_bits sense_bits;
+ if (pmic_get_sensors(&sense_bits)) {
+ printk(KERN_INFO "%s: pmic_get_sensors() failed\n",
+ __FUNCTION__);
+ return;
+ }
+ TRACE_MSG1(REMOVE_TCD, "MC13783 EVENT: se1: %d", sense_bits.sense_se1s);
+ otg_event(tcd_instance->otg,
+ (sense_bits.sense_se1s ? SE1_DET : SE1_DET_),
+ REMOVE_TCD, "MC13783 SE1");
+#else
+ otg_event(tcd_instance->otg,
+ (pmic_check_sense(sense_se1s) ? SE1_DET : SE1_DET_),
+ REMOVE_TCD, "MC13783 SE1");
+#endif
+#endif
+}
+
+
+
+
+void pmic_detect_event(const PMIC_CONVITY_EVENTS event)
+{
+ unsigned int flags = 0;
+
+ switch (event) {
+ case USB_DETECT_4V4_RISE:
+ flags &= ~(VBUS_VLD_);
+ flags |= VBUS_VLD;
+ break;
+ case USB_DETECT_4V4_FALL:
+ flags &= ~(VBUS_VLD);
+ flags |= VBUS_VLD_;
+ break;
+ case USB_DETECT_2V0_RISE:
+ flags &= ~(B_SESS_VLD_ | A_SESS_VLD_);
+ flags |= (B_SESS_VLD | A_SESS_VLD);
+ break;
+ case USB_DETECT_2V0_FALL:
+ flags &= ~(B_SESS_VLD | A_SESS_VLD);
+ flags |= (B_SESS_VLD_ | A_SESS_VLD_);
+ break;
+ case USB_DETECT_0V8_RISE:
+ flags &= ~(B_SESS_END_);
+ flags |= B_SESS_END;
+ break;
+ case USB_DM_HI:
+ flags &= ~(DM_HIGH);
+ flags |= DM_HIGH;
+ break;
+ case USB_DP_HI:
+ flags &= ~(DP_HIGH);
+ flags |= DP_HIGH;
+ break;
+ case USB_DETECT_0V8_FALL:
+ flags &= ~(B_SESS_END);
+ flags |= B_SESS_END_;
+ break;
+ case USB_DETECT_MINI_A:
+ flags &= ~(ID_GND);
+ flags |= ID_GND;
+ break;
+ case USB_DETECT_MINI_B:
+ flags &= ~(ID_FLOAT);
+ flags |= ID_FLOAT;
+ break;
+ case USB_DETECT_NON_USB_ACCESSORY:
+ flags &= (ID_FLOAT | ID_GND);
+ flags |= (ID_FLOAT_ | ID_GND_);
+ break;
+ case USB_DETECT_FACTORY_MODE:
+ flags &= (ID_FLOAT | ID_GND);
+ flags |= (ID_FLOAT | ID_GND);
+ break;
+ }
+#if 0
+ flags = (USB_DETECT_4V4_RISE ? VBUS_VLD : VBUS_VLD_) |
+ (USB_DETECT_2V0_RISE ? (B_SESS_VLD | A_SESS_VLD)
+ : (B_SESS_VLD_ | A_SESS_VLD_)) | (USB_DETECT_0V8_RISE ? B_SESS_END
+ : B_SESS_END_);
+#endif
+ pmic_otg_wakeup();
+}
+
+
+
+void mxc_pmic_mod_exit(struct otg_instance *otg)
+{
+
+ PMIC_STATUS rc;
+ if (pmic_work_task) {
+ otg_task_exit(pmic_work_task);
+ pmic_work_task = NULL;
+ }
+ if (pmic_otg_task) {
+ otg_task_exit(pmic_otg_task);
+ pmic_otg_task = NULL;
+ }
+ //while (PENDING_WORK_ITEM(pmic_work_bh)) {
+ // printk(KERN_ERR "%s: waiting for mc13783_work_bh\n",
+ // __FUNCTION__);
+ // schedule_timeout(10 * HZ);
+ //}
+ //while (PENDING_WORK_ITEM(pmic_otg_wq)) {
+ // printk(KERN_ERR "%s: waiting for mc13783_otg_wq\n",
+ // __FUNCTION__);
+ // schedule_timeout(10 * HZ);
+ //}
+
+
+ pmic_convity_set_mode(pmic_handle, RS232_1);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_USBCNTRL);
+
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_PU);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDP_PD);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_UDM_PD);
+ pmic_convity_clear_callback(pmic_handle);
+
+ rc = pmic_convity_close(pmic_handle);
+ if (rc != PMIC_SUCCESS) {
+ pr_debug("pmic_convity_close() returned error %d", rc);
+ }
+
+
+}
+
+/* ********************************************************************************************* */
+
+int mxc_pmic_mod_init(struct otg_instance *otg)
+{
+
+
+ PMIC_CONVITY_USB_TRANSCEIVER_MODE transceiver;
+ PMIC_STATUS rc = PMIC_ERROR;
+
+ rc = pmic_convity_open(&pmic_handle, USB);
+ if (rc != PMIC_SUCCESS) {
+ printk(KERN_INFO "Failed to connect to the transceiver\n");
+ }
+
+
+ TRACE_MSG0(REMOVE_TCD, "Setup the work item");
+ //PREPARE_WORK_ITEM(pmic_work_bh, &pmic_bh, NULL);
+ //PREPARE_WORK_ITEM(pmic_otg_wq, &pmic_otg_event_bh, NULL);
+
+ THROW_UNLESS((pmic_work_task = otg_task_init2("tcdwork", pmic_work_proc, otg, otg->tcd->TAG)), error);
+ THROW_UNLESS((pmic_otg_task = otg_task_init2("tcdotg", pmic_otg_proc, otg, otg->tcd->TAG)), error);
+
+ otg_task_start(pmic_work_task);
+ otg_task_start(pmic_otg_task);
+
+
+
+ if (pmic_convity_usb_get_xcvr(pmic_handle, &transceiver))
+ printk(KERN_INFO
+ "%s: pmic_convity_get_usb_transciver failed\n",
+ __FUNCTION__);
+
+ printk(KERN_INFO "%s: tw: %02x\n", __FUNCTION__, transceiver);
+
+
+ pmic_convity_set_callback(pmic_handle, pmic_detect_event,
+ 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 (rc != PMIC_SUCCESS) {
+ }
+
+ // XXX it may be more appropriate to do this in the enable function
+ // // and reset to something when disaabled
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_USBCNTRL);
+
+ start_flag = end_flag = 0;
+
+ CATCH(error) {
+
+ if (pmic_work_task) {
+ otg_task_exit(pmic_work_task);
+ pmic_work_task = NULL;
+ }
+ if (pmic_otg_task) {
+ otg_task_exit(pmic_otg_task);
+ pmic_otg_task = NULL;
+ }
+ return -EINVAL;
+ }
+ return 0;
+
+
+}
diff --git a/drivers/otg/hardware/mxc-procfs.c b/drivers/otg/hardware/mxc-procfs.c
new file mode 100644
index 000000000000..b5a3b3b22c5a
--- /dev/null
+++ b/drivers/otg/hardware/mxc-procfs.c
@@ -0,0 +1,772 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc-procfs.c - USB Device Core Layer
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/mxc/mxc-procfs.c|20070614183950|47700
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc-procfs.c
+ * @brief FREESCAELE Procfs register dump
+ *
+ *
+ * @ingroup FSOTG
+ *
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/pcd-include.h>
+#include <linux/module.h>
+//#include <asm/arch/mx2.h>
+
+//#include "mx2ads.h"
+//#include <otghw/mx2ads-hardware.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+
+/* ********************************************************************************************* */
+/*! char *fs_hnpstate - hnp state array */
+
+char * fs_hnpstate[32] = {
+ "b_idle", // 0
+ "b_master", // 1
+ "b_slave", // 2
+ "b_srp_init_v", // 3
+ "b_srb_init_d", // 4
+ "b_short_db", // 5
+ "b_wait_conn_a", // 6
+ "b_wait_conn_b", // 7
+ "b_wait_bus_lo", // 8
+ "", // 9
+ "b_wait_rst", // a
+ "", // b
+ "", // c
+ "", // d
+ "", // e
+ "", // f
+ "a_idle", // 0
+ "a_master", // 1
+ "a_slave", // 2
+ "a_wait_vpluse", // 3
+ "a_wait_dpulse", // 4
+ "a_wait_conn_a", // 5
+ "", // 6
+ "a_wait_conn_b", // 7
+ "a_wait_vrise", // 8
+ "a_suspend", // 9
+ "a_wait_vfall", // a
+ "a_vbus_err", // b
+ "conn_debounce", // c
+ "a_wait_abreq", // d
+ "a_wait_rst", // e
+ "", // f
+};
+
+/*! char *fs_core_regs - USBOTG Module Registers array */
+char * fs_core_regs[] = {
+ // USBOTG Module Registers
+ "OTG_CORE_HWMODE (0x000)",
+ "OTG_CORE_CINT_STAT (0x004)" ,
+ "OTG_CORE_CINT_STEN (0x008)" ,
+ "OTG_CORE_CLK_CTRL (0x00c)",
+ "OTG_CORE_RST_CTRL (0x010)",
+ "OTG_CORE_FRM_INTVL (0x014)",
+ "OTG_CORE_FRM_REMAIN (0x018)",
+ "OTG_CORE_HNP_CSTAT (0x01c)",
+ "OTG_CORE_HNP_TIMER1 (0x020)",
+ "OTG_CORE_HNP_TIMER2 (0x024)",
+ "OTG_CORE_HNP_T3PCR (0x028)",
+ "OTG_CORE_HINT_STAT (0x02c)",
+ "OTG_CORE_HINT_STEN (0x030)",
+ "OTG_CORE_CPUEPSEL_STAT (0x034)",
+ "UNUSED (0x038)",
+ "OTG_CORE_INTERRUPT_STEN (0x03c)",
+
+ // USB Function
+ "OTG_FUNC_CMD_STAT (0x040)", "OTG_FUNC_DEV_ADDR (0x044)", "OTG_FUNC_SINT_STAT (0x048)", "OTG_FUNC_SINT_STEN (0x04c)",
+ "OTG_FUNC_XINT_STAT (0x050)", "OTG_FUNC_YINT_STAT (0x054)", "OTG_FUNC_XYINT_STEN (0x058)", "OTG_FUNC_XFILL_STAT (0x05c)",
+ "OTG_FUNC_YFILL_STAT (0x060)", "OTG_FUNC_EP_EN (0x064)", "OTG_FUNC_EP_RDY (0x068)", "OTG_FUNC_IINT (0x06c)",
+ "OTG_FUNC_EP_DSTAT (0x070)", "OTG_FUNC_EP_DEN (0x074)", "OTG_FUNC_EP_TOGGLE (0x078)", "OTG_FUNC_FRM_NUM (0x07c)",
+
+ // USB Host
+ "OTG_HOST_CONTROL (0x080)", "OTG_HOST_SINT_STAT (0x084)", "OTG_HOST_SINT_STEN (0x088)", "HOST_UNUSED (0x08c)",
+ "HOST_UNUSED (0x090)", "OTG_HOST_XINT_STAT (0x094)", "OTG_HOST_YINT_STAT (0x098)", "HOST_UNUSED (0x09c)",
+ "OTG_HOST_XYINT_STEN (0x0a0)", "HOST_UNUSED (0x0a4)", "OTG_HOST_XFILL_STAT (0x0a8)", "OTG_HOST_YFILL_STAT (0x0ac)",
+ "HOST_UNUSED (0x0b0)", "HOST_UNUSED (0x0b4)", "HOST_UNUSED (0x0b8)", "HOST_UNUSED (0x0bc)",
+
+ // USB Host
+ "OTG_HOST_ETD_EN (0x0c0)", "HOST_UNUSED (0x0c4)", "OTG_HOST_DIR_ROUTE (0x0c8)", "OTG_HOST_IINT (0x0cc)",
+ "OTG_HOST_EP_DSTAT (0x0d0)", "OTG_HOST_ETD_DONE (0x0d4)", "HOST_UNUSED (0x0d8)", "HOST_UNUSED (0x0dc)",
+ "OTG_HOST_FRM_NUM (0x0e0)", "OTG_HOST_LSP_THRESH (0x0e4)", "OTG_HOST_HUB_DESCA (0x0e8)", "OTG_HOST_HUB_DESCB (0x0ec)",
+ "OTG_HOST_HUB_STAT (0x0f0)", "OTG_HOST_PORT_STATUS_1 (0x0f4)", "OTG_HOST_PORT_STATUS_2 (0x0f8)", "OTG_HOST_PORT_STATUS_3 (0x0fc)",
+
+};
+
+
+#if 0
+char *fs_i2c_regs1[] = {
+ "VENDOR_ID_LOW (0x100)", "VENDOR_ID_HIGH (0x101)",
+ "PRODUCT_ID_LOW (0x102)", "PRODUCT_ID_HIGH (0x103)",
+
+ "MODE_CONTROL_1_SET (0x104)", "MODE_CONTROL_1_CLR (0x105)",
+ "OTG_CONTROL_SET (0x106)", "OTG_CONTROL_CLR (0x107)",
+ "INTERRUPT_SOURCE (0x108)", "RESERVED (0x109)",
+ "INT_LAT_REG_SET (0x10a)", "INT_LAT_REG_CLR (0x10b)",
+ "INT_FALSE_REG_SET (0x10c)", "INT_FALSE_REG_CLR (0x10d)",
+ "INT_TRUE_REG_SET (0x10e)", "INT_TRUE_REG_CLR (0x10f)",
+ "OTG_STATUS (0x110)", "UNUSED (0x111)",
+ "MODE_CONTROL_2_SET (0x112)", "MODE_CONTROL_2_CLR (0x113)",
+
+ "BCD_DEV_LOW (0x114)", "BCD_DEV_HIGH (0x115)",
+
+ "RESERVED (0x116)", "RESERVED (0x117)",
+ "OTG_XCVR_DEVAD (0x118)", "SEQ_OP_REG (0x119)",
+ "SEQ_RD_STARTAD (0x11a)", "I2C_OP_CTRL_REG (0x11b)",
+ "RESERVED (0x11c)", "RESERVED (0x11d)",
+ "SCLK_TO_SCL_HPER (0x11e)", "I2C_INTERRUPT_AND_CTRL (0x11f)",
+};
+
+char *fs_i2c_regs2[] = {
+ "VENDOR_ID", "PRODUCT_ID",
+
+ "MODE_CONTROL_1_SETCLR", "OTG_CONTROL_SETCLR",
+ "INTERRUPT_SOURCE", "INT_LAT_REG_SETCLR",
+ "INT_FALSE_REG_SETCLR", "INT_TRUE_REG_SETCLR",
+ "OTG_STATUS", "MODE_CONTROL_2_SETCLR",
+
+ "BCD_DEV",
+};
+
+char *fs_i2c_regs3[] = {
+ "OTG_XCVR_DEVAD",
+ "SEQ_OP_REG",
+ "SEQ_RD_STARTAD",
+ "I2C_OP_CTRL_REG",
+ "RESERVED",
+ "RESERVED",
+ "SCLK_TO_SCL_HPER",
+ "I2C_INTERRUPT_AND_CTRL",
+};
+#endif
+
+/*! char* fs_etd_momory - etd memory array */
+char * fs_etd_memory[] = {
+ // 0x200 ETD Memory Access
+ "ETD 0 OUT WORD 0", "ETD 0 OUT WORD 1", "ETD 0 OUT WORD 2", "ETD 0 OUT WORD 3",
+ "ETD 0 IN WORD 0", "ETD 0 IN WORD 1", "ETD 0 IN WORD 2", "ETD 0 IN WORD 3",
+ "ETD 1 OUT WORD 0", "ETD 1 OUT WORD 1", "ETD 1 OUT WORD 2", "ETD 1 OUT WORD 3",
+ "ETD 1 IN WORD 0", "ETD 1 IN WORD 1", "ETD 1 IN WORD 2", "ETD 1 IN WORD 3",
+ "ETD 2 OUT WORD 0", "ETD 2 OUT WORD 1", "ETD 2 OUT WORD 2", "ETD 2 OUT WORD 3",
+ "ETD 2 IN WORD 0", "ETD 2 IN WORD 1", "ETD 2 IN WORD 2", "ETD 2 IN WORD 3",
+ "ETD 3 OUT WORD 0", "ETD 3 OUT WORD 1", "ETD 3 OUT WORD 2", "ETD 3 OUT WORD 3",
+ "ETD 3 IN WORD 0", "ETD 3 IN WORD 1", "ETD 3 IN WORD 2", "ETD 3 IN WORD 3",
+ "ETD 4 OUT WORD 0", "ETD 4 OUT WORD 1", "ETD 4 OUT WORD 2", "ETD 4 OUT WORD 3",
+ "ETD 4 IN WORD 0", "ETD 4 IN WORD 1", "ETD 4 IN WORD 2", "ETD 4 IN WORD 3",
+ "ETD 5 OUT WORD 0", "ETD 5 OUT WORD 1", "ETD 5 OUT WORD 2", "ETD 5 OUT WORD 3",
+ "ETD 5 IN WORD 0", "ETD 5 IN WORD 1", "ETD 5 IN WORD 2", "ETD 5 IN WORD 3",
+ "ETD 6 OUT WORD 0", "ETD 6 OUT WORD 1", "ETD 6 OUT WORD 2", "ETD 6 OUT WORD 3",
+ "ETD 6 IN WORD 0", "ETD 6 IN WORD 1", "ETD 6 IN WORD 2", "ETD 6 IN WORD 3",
+ "ETD 7 OUT WORD 0", "ETD 7 OUT WORD 1", "ETD 7 OUT WORD 2", "ETD 7 OUT WORD 3",
+ "ETD 7 IN WORD 0", "ETD 7 IN WORD 1", "ETD 7 IN WORD 2", "ETD 7 IN WORD 3",
+ "ETD 8 OUT WORD 0", "ETD 8 OUT WORD 1", "ETD 8 OUT WORD 2", "ETD 8 OUT WORD 3",
+ "ETD 8 IN WORD 0", "ETD 8 IN WORD 1", "ETD 8 IN WORD 2", "ETD 8 IN WORD 3",
+ "ETD 9 OUT WORD 0", "ETD 9 OUT WORD 1", "ETD 9 OUT WORD 2", "ETD 9 OUT WORD 3",
+ "ETD 9 IN WORD 0", "ETD 9 IN WORD 1", "ETD 9 IN WORD 2", "ETD 9 IN WORD 3",
+ "ETD A OUT WORD 0", "ETD A OUT WORD 1", "ETD A OUT WORD 2", "ETD A OUT WORD 3",
+ "ETD A IN WORD 0", "ETD A IN WORD 1", "ETD A IN WORD 2", "ETD A IN WORD 3",
+ "ETD B OUT WORD 0", "ETD B OUT WORD 1", "ETD B OUT WORD 2", "ETD B OUT WORD 3",
+ "ETD B IN WORD 0", "ETD B IN WORD 1", "ETD B IN WORD 2", "ETD B IN WORD 3",
+ "ETD C OUT WORD 0", "ETD C OUT WORD 1", "ETD C OUT WORD 2", "ETD C OUT WORD 3",
+ "ETD C IN WORD 0", "ETD C IN WORD 1", "ETD C IN WORD 2", "ETD C IN WORD 3",
+ "ETD D OUT WORD 0", "ETD D OUT WORD 1", "ETD D OUT WORD 2", "ETD D OUT WORD 3",
+ "ETD D IN WORD 0", "ETD D IN WORD 1", "ETD D IN WORD 2", "ETD D IN WORD 3",
+ "ETD E OUT WORD 0", "ETD E OUT WORD 1", "ETD E OUT WORD 2", "ETD E OUT WORD 3",
+ "ETD E IN WORD 0", "ETD E IN WORD 1", "ETD E IN WORD 2", "ETD E IN WORD 3",
+ "ETD F OUT WORD 0", "ETD F OUT WORD 1", "ETD F OUT WORD 2", "ETD F OUT WORD 3",
+ "ETD F IN WORD 0", "ETD F IN WORD 1", "ETD F IN WORD 2", "ETD F IN WORD 3",
+};
+
+/*! char * fs_ep_memory - endpoint memory array */
+char * fs_ep_memory[] = {
+ // 0x400 EP Memory Access
+
+ "EP 0 OUT WORD 0", "EP 0 OUT WORD 1", "EP 0 OUT WORD 2", "EP 0 OUT WORD 3",
+ "EP 0 IN WORD 0", "EP 0 IN WORD 1", "EP 0 IN WORD 2", "EP 0 IN WORD 3",
+ "EP 1 OUT WORD 0", "EP 1 OUT WORD 1", "EP 1 OUT WORD 2", "EP 1 OUT WORD 3",
+ "EP 1 IN WORD 0", "EP 1 IN WORD 1", "EP 1 IN WORD 2", "EP 1 IN WORD 3",
+ "EP 2 OUT WORD 0", "EP 2 OUT WORD 1", "EP 2 OUT WORD 2", "EP 2 OUT WORD 3",
+ "EP 2 IN WORD 0", "EP 2 IN WORD 1", "EP 2 IN WORD 2", "EP 2 IN WORD 3",
+ "EP 3 OUT WORD 0", "EP 3 OUT WORD 1", "EP 3 OUT WORD 2", "EP 3 OUT WORD 3",
+ "EP 3 IN WORD 0", "EP 3 IN WORD 1", "EP 3 IN WORD 2", "EP 3 IN WORD 3",
+ "EP 4 OUT WORD 0", "EP 4 OUT WORD 1", "EP 4 OUT WORD 2", "EP 4 OUT WORD 3",
+ "EP 4 IN WORD 0", "EP 4 IN WORD 1", "EP 4 IN WORD 2", "EP 4 IN WORD 3",
+ "EP 5 OUT WORD 0", "EP 5 OUT WORD 1", "EP 5 OUT WORD 2", "EP 5 OUT WORD 3",
+ "EP 5 IN WORD 0", "EP 5 IN WORD 1", "EP 5 IN WORD 2", "EP 5 IN WORD 3",
+ "EP 6 OUT WORD 0", "EP 6 OUT WORD 1", "EP 6 OUT WORD 2", "EP 6 OUT WORD 3",
+ "EP 6 IN WORD 0", "EP 6 IN WORD 1", "EP 6 IN WORD 2", "EP 6 IN WORD 3",
+ "EP 7 OUT WORD 0", "EP 7 OUT WORD 1", "EP 7 OUT WORD 2", "EP 7 OUT WORD 3",
+ "EP 7 IN WORD 0", "EP 7 IN WORD 1", "EP 7 IN WORD 2", "EP 7 IN WORD 3",
+ "EP 8 OUT WORD 0", "EP 8 OUT WORD 1", "EP 8 OUT WORD 2", "EP 8 OUT WORD 3",
+ "EP 8 IN WORD 0", "EP 8 IN WORD 1", "EP 8 IN WORD 2", "EP 8 IN WORD 3",
+ "EP 9 OUT WORD 0", "EP 9 OUT WORD 1", "EP 9 OUT WORD 2", "EP 9 OUT WORD 3",
+ "EP 9 IN WORD 0", "EP 9 IN WORD 1", "EP 9 IN WORD 2", "EP 9 IN WORD 3",
+ "EP A OUT WORD 0", "EP A OUT WORD 1", "EP A OUT WORD 2", "EP A OUT WORD 3",
+ "EP A IN WORD 0", "EP A IN WORD 1", "EP A IN WORD 2", "EP A IN WORD 3",
+ "EP B OUT WORD 0", "EP B OUT WORD 1", "EP B OUT WORD 2", "EP B OUT WORD 3",
+ "EP B IN WORD 0", "EP B IN WORD 1", "EP B IN WORD 2", "EP B IN WORD 3",
+ "EP C OUT WORD 0", "EP C OUT WORD 1", "EP C OUT WORD 2", "EP C OUT WORD 3",
+ "EP C IN WORD 0", "EP C IN WORD 1", "EP C IN WORD 2", "EP C IN WORD 3",
+ "EP D OUT WORD 0", "EP D OUT WORD 1", "EP D OUT WORD 2", "EP D OUT WORD 3",
+ "EP D IN WORD 0", "EP D IN WORD 1", "EP D IN WORD 2", "EP D IN WORD 3",
+ "EP E OUT WORD 0", "EP E OUT WORD 1", "EP E OUT WORD 2", "EP E OUT WORD 3",
+ "EP E IN WORD 0", "EP E IN WORD 1", "EP E IN WORD 2", "EP E IN WORD 3",
+ "EP F OUT WORD 0", "EP F OUT WORD 1", "EP F OUT WORD 2", "EP F OUT WORD 3",
+ "EP F IN WORD 0", "EP F IN WORD 1", "EP F IN WORD 2", "EP F IN WORD 3",
+};
+
+/*! char * fs_control_regs - control registers array */
+char * fs_control_regs[] = {
+ // 0x600 USB Control Registers
+ "OTG_SYS_CTRL",
+};
+
+/*! char * fs_dma_regs - dma registers array */
+char * fs_dma_regs[] = {
+ // 0x800 DMA Registers
+ "OTG_DMA_REV_NUM (0x800)", "OTG_DMA_DINT_STAT (0x804)", "OTG_DMA_DINT_STEN (0x808)", "OTG_DMA_ETD_ERR (0x80c)",
+ "OTG_DMA_EP_ERR (0x810)", "DMA UNUSED (0x814)", "DMA UNUSED (0x818)", "DMA UNUSED (0x81c)",
+ "OTG_DMA_ETD_EN (0x820)", "OTG_DMA_EP_EN (0x824)", "OTG_DMA_ETD_ENXREQ (0x828)", "OTG_DMA_EP_ENXREQ (0x82c)",
+ "OTG_DMA_ETD_ENXYREQ (0x830)", "OTG_DMA_EP_ENXYREQ (0x834)", "OTG_DMA_ETD_BURST4 (0x838)", "OTG_DMA_EP_BURST4 (0x83c)",
+ "OTG_DMA_MISC_CTRL (0x840)", "OTG_DMA_ETD_CH_CLR (0x844)", "OTG_DMA_EP_CH_CLR (0x848)", "DMA UNUSED (0x84c)",
+};
+
+char * fs_ep_msa[] = {
+ "EP 0 OUT MSA", "EP 0 IN MSA",
+ "EP 1 OUT MSA", "EP 1 IN MSA",
+ "EP 2 OUT MSA", "EP 2 IN MSA",
+ "EP 3 OUT MSA", "EP 3 IN MSA",
+ "EP 4 OUT MSA", "EP 4 IN MSA",
+ "EP 5 OUT MSA", "EP 5 IN MSA",
+ "EP 6 OUT MSA", "EP 6 IN MSA",
+ "EP 7 OUT MSA", "EP 7 IN MSA",
+ "EP 8 OUT MSA", "EP 8 IN MSA",
+ "EP 9 OUT MSA", "EP 9 IN MSA",
+ "EP A OUT MSA", "EP A IN MSA",
+ "EP B OUT MSA", "EP B IN MSA",
+ "EP C OUT MSA", "EP C IN MSA",
+ "EP D OUT MSA", "EP D IN MSA",
+ "EP E OUT MSA", "EP E IN MSA",
+ "EP F OUT MSA", "EP F IN MSA",
+};
+
+char *fs_ureg_name(u32 port)
+{
+ u32 low = port & 0xfff;
+
+ //TRACE_MSG32("PORT: %x", (int)port);
+
+ port = (port >> 2) & 0xfff;
+
+ //TRACE_MSG32("PORT: %x", (int)port);
+
+ if ((int)port < (0x100 >> 2))
+ return (port < (sizeof(fs_core_regs)/4)) ? fs_core_regs[port] : "unknown";
+
+ port -= 0x100 >> 2;
+
+ if ((int)port < (0x100 >> 2)) {
+ low &= 0x1f;
+ #if 0
+ if (low < 0x18) {
+ low >>= 2;
+ return (low < (sizeof(fs_i2c_regs1)/4)) ? fs_i2c_regs1[low] : "unknown";
+ }
+ low -= 0x18;
+ return (low < sizeof(fs_i2c_regs3)/4) ? fs_i2c_regs3[low] : "unknown";
+ return (low < sizeof(fs_i2c_regs1)/4) ? fs_i2c_regs1[low] : "unknown";
+ #endif
+ return "unknown";
+ }
+
+ port -= 0x100 >> 2;
+
+ if ((int)port < (0x200 >> 2))
+ return (port < (sizeof(fs_etd_memory)/4)) ? fs_etd_memory[port] : "unknown";
+
+ port -= 0x200 >> 2;
+
+ if ((int)port < (0x200 >> 2))
+ return (port < (sizeof(fs_ep_memory)/4)) ? fs_ep_memory[port] : "unknown";
+
+ port -= 0x200 >> 2;
+
+ if ((int)port < (0x200 >> 2))
+ return (port < (sizeof(fs_control_regs)/4)) ? fs_control_regs[port] : "unknown";
+
+ port -= 0x200 >> 2;
+
+ if ((int)port < (0x180>>2))
+ return (port < (sizeof(fs_dma_regs)/4)) ? fs_dma_regs[port] : "unknown";
+
+ port -= 0x180 >> 2;
+
+ if ((int)port < (0x100>>2))
+ return (port < (sizeof(fs_ep_msa)/4)) ? fs_ep_msa[port] : "unknown";
+
+ return "unknown";
+
+}
+
+
+/* Proc Filesystem *************************************************************************** */
+
+
+
+u8 udc_regs1[0x1000];
+u8 udc_regs2[0x1000];
+
+u32 zeros[100];
+
+
+
+void fs_copy(u8 *dp)
+{
+ fs_memcpy32((u32 *) (dp + 0x000), (u32 *)IO_ADDRESS(OTG_CORE_BASE), 0x100/4);
+ //fs_memcpy ((u8 *) (dp + 0x100), (u8 *)IO_ADDRESS(OTG_I2C_BASE), 0x100);
+ fs_memcpy32((u32 *) (dp + 0x200), (u32 *)IO_ADDRESS(OTG_ETD_BASE), 0xe00/4);
+}
+void fs_clear(volatile u32 *addr, int words)
+{
+ while (words--) *addr++ = 0;
+}
+
+
+/* *
+ * dohex
+ *
+ */
+static void dohexdigit (char *cp, unsigned char val)
+{
+ if (val < 0xa) {
+ *cp = val + '0';
+ } else if ((val >= 0x0a) && (val <= 0x0f)) {
+ *cp = val - 0x0a + 'a';
+ }
+}
+
+/* *
+ * dohex
+ *
+ */
+static void dohexval (char *cp, unsigned char val)
+{
+ dohexdigit (cp++, val >> 4);
+ dohexdigit (cp++, val & 0xf);
+}
+
+int fs_regl(char *page, u32 reg)
+{
+ u32 offset = reg & 0xfff;
+ u32 *p1 = (u32 *) (udc_regs1 + offset);
+ u32 *p2 = (u32 *) (udc_regs2 + offset);
+
+ if (*p1 == *p2)
+ return sprintf (page, " %-34s [%03x]: %08x\n", fs_ureg_name(reg), reg & 0xfff, *p2);
+ else
+ return sprintf (page, " %-34s [%03x]: %08x (%08x)\n", fs_ureg_name(reg), reg & 0xfff, *p2, *p1);
+}
+
+int fs_regb(char *page, u32 reg)
+{
+ u32 offset = reg & 0xfff;
+ u8 *p1 = (u8 *) (udc_regs1 + offset);
+ u8 *p2 = (u8 *) (udc_regs2 + offset);
+
+ if (*p1 == *p2)
+ return sprintf (page, " %-34s [%03x]: %02x\n", fs_ureg_name(reg), reg & 0xfff, *p2);
+ else
+ return sprintf (page, " %-34s [%03x]: %02x (%02x)\n", fs_ureg_name(reg), reg & 0xfff, *p2, *p1);
+}
+
+int fs_regb2(char *page, u32 reg)
+{
+ u32 offset = reg & 0xfff;
+ u8 *p1 = (u8 *) (udc_regs1 + offset);
+ u8 *p2 = (u8 *) (udc_regs2 + offset);
+
+ if (!memcmp(p1, p2, 2))
+ return sprintf (page, " %-34s [%03x]: %02x %02x\n",
+ fs_ureg_name(reg), reg & 0xfff, p2[1], p2[0]);
+ else
+ return sprintf (page, " %-34s [%03x]: %02x %02x (%02x %02x)\n",
+ fs_ureg_name(reg+1), reg & 0xfff,
+ p2[1], p2[0], p1[1], p1[0]);
+}
+
+int fs_regb4(char *page, u32 reg)
+{
+ u32 offset = reg & 0xfff;
+ u8 *p1 = (u8 *) (udc_regs1 + offset);
+ u8 *p2 = (u8 *) (udc_regs2 + offset);
+
+ if (!memcmp(p1, p2, 4))
+ return sprintf (page, " %-34s [%03x]: %02x %02x %02x %02x\n",
+ fs_ureg_name(reg), reg & 0xfff, p2[3], p2[2], p2[1], p2[0]);
+ else
+ return sprintf (page, " %-34s [%03x]: %02x %02x %02x %02x (%02x %02x %02x %02x)\n",
+ fs_ureg_name(reg), reg & 0xfff,
+ p2[3], p2[2], p2[1], p2[0], p1[3], p1[2], p1[1], p1[0]);
+}
+
+int fs_etd(char *page, u32 reg)
+{
+ u32 offset = reg & 0xfff;
+ u32 *p1 = (u32 *) (udc_regs1 + offset);
+ u32 *p2 = (u32 *) (udc_regs2 + offset);
+
+ if (!memcmp(p1, p2, 16)) {
+ if ((reg == OTG_ETD_BASE) || memcmp(zeros, p2, 16))
+ return sprintf (page, " %-34s [%03x]: %08x %08x %08x %08x\n",
+ fs_ureg_name(reg), reg & 0xfff, p2[3], p2[2], p2[1], p2[0]);
+ return 0;
+ }
+ else
+ return sprintf (page, " %-34s [%03x]: %08x %08x %08x %08x (%08x %08x %08x %08x)\n",
+ fs_ureg_name(reg), reg & 0xfff,
+ p2[3], p2[2], p2[1], p2[0], p1[3], p1[2], p1[1], p1[0]);
+}
+
+int fs_ep(char *page, u32 reg)
+{
+ u32 offset = reg & 0x7ff;
+ u32 *p1 = (u32 *) (udc_regs1 + offset);
+ u32 *p2 = (u32 *) (udc_regs2 + offset);
+
+ if (!memcmp(p1, p2, 16)) {
+ if (memcmp(zeros, p2, 16))
+ return sprintf (page, " %-34s [%03x]: %08x %08x %08x %08x\n",
+ fs_ureg_name(reg), reg & 0xfff, p2[0], p2[1], p2[2], p2[3]);
+ return 0;
+ }
+ else
+ return sprintf (page, " %-34s [%03x]: %08x %08x %08x %08x (%08x %08x %08x %08x)\n",
+ fs_ureg_name(reg), reg & 0xfff,
+ p2[0], p2[1], p2[2], p2[3], p1[0], p1[1], p1[2], p1[3]);
+}
+/*! dump_mem - dump mem information
+ * @param page - buffer to save information
+ * @param dp - memory point to dump
+ * @param count - message length to dump
+ */
+int dump_mem(char *page, u8 * dp, int count)
+{
+ return sprintf (page,
+ "[%08x] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x \n",
+ dp,
+ dp[0], dp[1], dp[2], dp[3], dp[4], dp[5], dp[6], dp[7],
+ dp[8], dp[9], dp[10], dp[11], dp[12], dp[13], dp[14], dp[15]
+ );
+}
+
+/*! fs_cstat -
+ * @param page
+ */
+
+static int fs_cstat(char *page)
+{
+ u32 hnp_cstat = fs_rl(OTG_CORE_HNP_CSTAT);
+ u32 cint_stat = fs_rl(OTG_CORE_CINT_STAT);
+ u32 hint_stat = fs_rl(OTG_CORE_HINT_STAT);
+ int len = 0;
+
+ //TRACE_MSG32("cint_stat: %08x", cint_stat);
+ //TRACE_MSG32("hnp_cstat: %08x", hnp_cstat);
+ //TRACE_MSG32("hint_stat: %08x", hint_stat);
+ //
+ len += sprintf(page + len, "\nHNP Status: ");
+
+ if (hnp_cstat & MODULE_HNPDAT) len += sprintf(page + len, "HNPDAT ");
+ if (hnp_cstat & MODULE_VBUSBSE) len += sprintf(page + len, "VBUSBSE ");
+ if (hnp_cstat & MODULE_VBUSABSV) len += sprintf(page + len, "VBUSABSV ");
+ if (hnp_cstat & MODULE_VBUSGTAVV) len += sprintf(page + len, "VBUSGTAVV ");
+
+ if (hnp_cstat & MODULE_ARMTHNPE) len += sprintf(page + len, "ARMTHNPE ");
+ if (hnp_cstat & MODULE_BHNPEN) len += sprintf(page + len, "BHNPEN ");
+
+ if (hnp_cstat & MODULE_SLAVE) len += sprintf(page + len, "SLAVE ");
+ if (hnp_cstat & MODULE_MASTER) len += sprintf(page + len, "MASTER ");
+ if (hnp_cstat & MODULE_BGEN) len += sprintf(page + len, "BGEN ");
+ if (hnp_cstat & MODULE_CMPEN) len += sprintf(page + len, "CMPEN ");
+ if (hnp_cstat & MODULE_ISBDEV) len += sprintf(page + len, "ISBDEV ");
+ if (hnp_cstat & MODULE_ISADEV) len += sprintf(page + len, "ISADEV");
+
+ if (hnp_cstat & MODULE_SWVBUSPUL) len += sprintf(page + len, "SWVBUSPUL ");
+ if (hnp_cstat & MODULE_SWAUTORST) len += sprintf(page + len, "SWAUTORS T");
+ if (hnp_cstat & MODULE_SWPUDP) len += sprintf(page + len, "SWPUDP ");
+
+ len += sprintf(page + len, "HNPSTAT: %02x ", (hnp_cstat >> 4) & 0x1f);
+
+ if (hnp_cstat & MODULE_CLRERROR) len += sprintf(page + len, "CLRERROR ");
+ if (hnp_cstat & MODULE_ADROPBUS) len += sprintf(page + len, "ADROPBUS ");
+ if (hnp_cstat & MODULE_ABBUSREQ) len += sprintf(page + len, "ABBUSREQ ");
+
+ len += sprintf(page + len, "\n");
+ return len;
+}
+
+/* *
+ * fs_device_proc_read - implement proc file system read.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return
+ * statistics for.
+ */
+static ssize_t fs_device_proc_read_functions (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ int index;
+ int i;
+ u32 r;
+
+ u8 config_descriptor[512];
+ int config_size;
+
+ //struct list_head *lhd;
+
+ // get a page, max 4095 bytes of data...
+ if (!(page = GET_KERNEL_PAGE())) {
+ return -ENOMEM;
+ }
+
+ len = 0;
+ index = (*pos)++;
+
+ switch(index) {
+
+ case 0:
+ len += sprintf ((char *) page + len, "MX21 OTG Dump\n");
+ fs_copy(udc_regs2);
+
+ r = udc_regs2[0];
+ switch (r & MODULE_CRECFG) {
+ case 0: len += sprintf ((char *) page + len, "Hardware HNP\n"); break;
+ case 1: len += sprintf ((char *) page + len, "Host Only\n"); break;
+ case 2: len += sprintf ((char *) page + len, "Function Only\n"); break;
+ case 3: len += sprintf ((char *) page + len, "Software HNP\n"); break;
+ }
+ len += sprintf ((char *) page + len, "OTG Transceiver: ");
+ switch (((r & MODULE_OTGXCVR) >> 6) & MODULE_CRECFG) {
+ case 0: len += sprintf ((char *) page + len, "Differential / Differential\n"); break;
+ case 2: len += sprintf ((char *) page + len, "Single-Ended / Differential\n"); break;
+ case 1: len += sprintf ((char *) page + len, "Differential / Single-Ended\n"); break;
+ case 3: len += sprintf ((char *) page + len, "Single-Ended / Single-Ended\n"); break;
+ }
+ break;
+
+ case 1:
+ len += sprintf ((char *) page + len, "\nUSB Control\n");
+ len += fs_regl((char *) page + len, OTG_SYS_CTRL);
+ break;
+
+ case 2:
+ len += sprintf ((char *) page + len, "\nUSB Core\n");
+ for (i = OTG_CORE_HWMODE; i <= OTG_CORE_INTERRUPT_STEN; i += 4)
+ len += fs_regl((char *) page + len, i);
+ len += fs_cstat((char *)page + len);
+ break;
+
+ case 3:
+ len += sprintf ((char *) page + len, "\nUSB Host\n");
+ for (i = OTG_HOST_CONTROL; i <= OTG_HOST_PORT_STATUS_3; i += 4)
+ len += fs_regl((char *) page + len, i);
+ break;
+
+ case 4:
+ len += sprintf ((char *) page + len, "\nUSB ETD\n");
+ for (i = OTG_ETD_BASE; i < OTG_EP_BASE; i += 16)
+ len += fs_etd((char *) page + len, i);
+ break;
+
+ case 5:
+ len += sprintf ((char *) page + len, "\nUSB Func\n");
+ for (i = OTG_FUNC_CMD_STAT; i <= OTG_FUNC_FRM_NUM; i += 4)
+ len += fs_regl((char *) page + len, i);
+ break;
+
+ case 6:
+ len += sprintf ((char *) page + len, "\nUSB EP\n");
+ //for (i = OTG_EP_BASE; i < OTG_SYS_BASE; i += 16)
+ // len += fs_ep((char *) page + len, i);
+ for (i = OTG_EP_BASE; i < 8; i++)
+ len += fs_ep((char *) page + len, i*16);
+ break;
+
+ case 7:
+ len += sprintf ((char *) page + len, "\nUSB DMA\n");
+ for (i = OTG_DMA_REV_NUM; i <= OTG_DMA_EP_CH_CLR; i += 4)
+ len += fs_regl((char *) page + len, i);
+ break;
+ case 8:
+ len += sprintf ((char *) page + len, "\nUSB DMA MSA\n");
+ for (i = OTG_DMA_EPN_MSA(0); i <= OTG_DMA_EPN_MSA(32); i += 4)
+ len += fs_regl((char *) page + len, i);
+ break;
+ case 9:
+ len += sprintf ((char *) page + len, "\nUSB Bufs\n");
+ break;
+
+ case 10:
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_OUT), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_OUT)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_OUT)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_OUT)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_OUT), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_OUT)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_OUT)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_OUT)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ break;
+
+ case 11:
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_IN), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_IN)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_IN)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(0, USB_DIR_IN)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_IN), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_IN)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_IN)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(0, USB_DIR_IN)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ break;
+
+ case 12:
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_OUT), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_OUT)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_OUT)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_OUT)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_OUT), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_OUT)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_OUT)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_OUT)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ break;
+
+ case 13:
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_IN), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_IN)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_IN)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_x_address(1, USB_DIR_IN)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_IN), 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_IN)+16, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_IN)+32, 16);
+ len += dump_mem((char *) page + len, (u8 *)data_y_address(1, USB_DIR_IN)+48, 16);
+ len += sprintf ((char *) page + len, "\n");
+ break;
+ #if 0
+ case 9:
+ len += sprintf ((char *) page + len, "\nUSB OTG\n");
+ for (i = OTG_I2C_BASE; i < MX2_OTG_XCVR_DEVAD; i += 1)
+ len += fs_regb((char *) page + len, i);
+ break;
+ case 10:
+ len += sprintf ((char *) page + len, "\nUSB I2C Control\n");
+ for (i = MX2_OTG_XCVR_DEVAD; i <= MX2_I2C_INTERRUPT_AND_CTRL; i += 1)
+ len += fs_regb((char *) page + len, i);
+ break;
+ #endif
+
+
+
+
+
+ default:
+ memcpy(udc_regs1, udc_regs2, sizeof(udc_regs1));
+ break;
+ }
+
+ //printk(KERN_INFO"%s: len: %d count: %d\n", __FUNCTION__, len, count);
+
+ if (len > count) {
+ //printk(KERN_INFO"%s: len > count\n", __FUNCTION__);
+ //printk(KERN_INFO"%s", page);
+ len = -EINVAL;
+ }
+ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) {
+ //printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__);
+ len = -EFAULT;
+ }
+ else {
+ //printk(KERN_INFO"%s: OK\n", __FUNCTION__);
+ }
+ free_page (page);
+ return len;
+}
+
+/*! struct file_operations fs_device_proc_operations_functions */
+static struct file_operations fs_device_proc_operations_functions = {
+ read:fs_device_proc_read_functions,
+};
+
+
+
+
+/* Module init ******************************************************************************* */
+
+
+int mxc_procfs_init (void)
+{
+ {
+ struct proc_dir_entry *p;
+
+ // create proc filesystem entries
+ if ((p = create_proc_entry ("mxclib", 0, 0)) == NULL)
+ return -ENOMEM;
+ p->proc_fops = &fs_device_proc_operations_functions;
+ }
+
+ //printk(KERN_INFO"%s: %08x", __FUNCTION__, IO_ADDRESS(OTG_ETD_BASE));
+ //fs_clear((volatile u32 *)IO_ADDRESS(OTG_ETD_BASE), 0x200);
+ //fs_clear((volatile u32 *)IO_ADDRESS(OTG_EP_BASE), 0x200);
+ fs_copy(udc_regs1);
+ memset(udc_regs1 + 0x18, 0, 4);
+
+ return 0;
+}
+
+void mxc_procfs_exit (void)
+{
+ // remove proc filesystem entry
+ remove_proc_entry ("mxclib", NULL);
+}
+
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/hardware/mxc91321-gpio.c b/drivers/otg/hardware/mxc91321-gpio.c
new file mode 100644
index 000000000000..df925771a05c
--- /dev/null
+++ b/drivers/otg/hardware/mxc91321-gpio.c
@@ -0,0 +1,217 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc91321-gpio.c - Freescale USBOTG ArgonLV gpio and iomux setting
+ * @(#) sp/root@belcarra.com/debian-black.(none)|otg/platform/mxc/mxc91321-gpio.c|20070822230929|24222
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>,
+ * Shahrad Payandeh <sp@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc91321-gpio.c
+ * @brief Freescale USB Host Controller Driver
+ *
+ * @ingroup FSOTG
+ */
+
+#include <linux/irq.h>
+#include <otg/pcd-include.h>
+#include <../arch/arm/mach-mxc91321/iomux.h>
+
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#define ZGPIO_PORT 0
+#define ZGPIO_PIN 2
+
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301) || defined(_OTG_DOXYGEN)
+
+BOOL zasevb_int_disabled = FALSE;
+/* ********************************************************************************************* */
+/*!
+ * zasevb_gpio_int_hndlr() - gpio interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ * @return interrupt handler status
+ * This disables the gpio interrup and schedules the isp1301 bottom half handler.
+ *
+ */
+static irqreturn_t zasevb_gpio_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ disable_irq(IOMUX_TO_IRQ(PIN_GPIO2));
+ zasevb_int_disabled = TRUE;
+
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: SCHEDULE WORK");
+ // printk(KERN_INFO"%s:\n", __FUNCTION__);
+ isp1301_bh_wakeup(REMOVE_tcd_instance->otg, FALSE);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * zasevb_isp1301_bh()- call isp1301 bottom half handler
+ * @param arg
+ * This is a wrapper to the isp1301 bottom half handler, it
+ * re-enables the gpio interrupt after processing complete.
+ */
+void *zasevb_isp1301_bh(void *arg)
+{
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: ISP1301_BH");
+ isp1301_bh(arg);
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: REENABLE");
+
+ if (zasevb_int_disabled) {
+ zasevb_int_disabled = FALSE;
+ enable_irq(IOMUX_TO_IRQ(PIN_GPIO2));
+ }
+ return NULL;
+}
+
+
+
+/*! mxc_iomux_gpio_isp1301_set
+ * This function set iomux settings depends on platform and usb mode.
+ *
+ * @param otg - otg instance
+ * @param usb_mode setting usb mode.
+ *
+ */
+
+int mxc_iomux_gpio_isp1301_set (struct otg_instance *otg, int usb_mode)
+{
+
+ int gpio = 1;
+
+ printk (KERN_INFO"MXC gpio setting for isp1301\n");
+
+ isp1301_mod_init(otg, &zasevb_isp1301_bh);
+
+ TRACE_MSG0(REMOVE_TCD, "5. IOMUX and GPIO Interrupt Configuration");
+ iomux_config_mux(PIN_GPIO2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ //Setting interrupt for ISP1301
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ set_irq_type(IOMUX_TO_IRQ(PIN_GPIO2), IRQF_TRIGGER_FALLING);
+ #else
+ set_irq_type(IOMUX_TO_IRQ(PIN_GPIO2), IRQT_FALLING);
+ #endif
+ gpio = request_irq(IOMUX_TO_IRQ(PIN_GPIO2), zasevb_gpio_int_hndlr,
+ 0, "ISP1301", (void *)&ocd_ops);
+ THROW_IF(gpio, error);
+
+
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ UNLESS (gpio) gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+/*! mxc_iomux_gpio_isp1301_reset - clear isp1301 iomux settings
+ */
+int mxc_iomux_gpio_isp1301_reset (void)
+{
+ free_irq(IOMUX_TO_IRQ(PIN_GPIO2), &ocd_ops);
+ return 0;
+}
+
+#endif //CONFIG_OTG_ZASEVB_ISP1301
+
+
+
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+/*! mxc_iomux_gpio_mc13783_set - set mc13783 iomux settings
+ * @param usb_mode
+ * @return 0
+ */
+int mxc_iomux_gpio_mc13783_set (int usb_mode)
+{
+
+ printk (KERN_INFO"MXC gpio setting for Atlas\n");
+
+ printk(KERN_INFO"IOMUX setting for Argon+\n");
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+
+ return 0;
+}
+
+/*! mxc_iomux_gpio_mc13783_reset - clear mc13783 iomux settings
+ */
+int mxc_iomux_gpio_mc13783_reset (void)
+{
+
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ return 0;
+}
+#endif //CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY
+
+
+int mxc_host_gpio (void)
+{
+ iomux_config_mux(PIN_GPIO5, OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1); // USB1_OC
+ printk(KERN_INFO"%s: NONE/GPIO\n", __FUNCTION__);
+ return 0;
+}
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_reset);
+#endif
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_mc13783_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_mc13783_reset);
+#endif
+
+OTG_EXPORT_SYMBOL(mxc_host_gpio);
diff --git a/drivers/otg/hardware/mxc91331-gpio.c b/drivers/otg/hardware/mxc91331-gpio.c
new file mode 100644
index 000000000000..3fa472fc059d
--- /dev/null
+++ b/drivers/otg/hardware/mxc91331-gpio.c
@@ -0,0 +1,215 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/mxc91331-gpio.c - Freescale USBOTG ArgonPlus gpio and iomux setting
+ * @(#) sp/root@belcarra.com/debian-black.(none)|otg/platform/mxc/mxc91331-gpio.c|20070822230929|09899
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>,
+ * Shahrad Payandeh <sp@belcarra.com>
+ */
+/*!
+ * @file otg/hardware/mxc91331-gpio.c
+ * @brief Freescale USB Host Controller Driver
+ *
+ * @ingroup FSOTG
+ */
+
+#include <linux/irq.h>
+#include <otg/pcd-include.h>
+#include <../arch/arm/mach-mxc91321/iomux.h>
+
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+#include <asm/arch/gpio.h>
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+#endif
+
+#define ZGPIO_PORT 0
+#define ZGPIO_PIN 2
+
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301) || defined(_OTG_DOXYGEN)
+
+BOOL zasevb_int_disabled = FALSE;
+
+/* ********************************************************************************************* */
+/*!
+ * zasevb_gpio_int_hndlr() - gpio interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ * @return interrupt handler status
+ * This disables the gpio interrup and schedules the isp1301 bottom half handler.
+ *
+ */
+static irqreturn_t zasevb_gpio_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ disable_irq(irq);
+ zasevb_int_disabled = TRUE;
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: SCHEDULE WORK");
+ isp1301_bh_wakeup(REMOVE_tcd_instance->otg, FALSE);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * zasevb_isp1301_bh()- call isp1301 bottom half handler
+ * @param arg
+ * This is a wrapper to the isp1301 bottom half handler, it
+ * re-enables the gpio interrupt after processing complete.
+ */
+void *zasevb_isp1301_bh(void *arg)
+{
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: ISP1301_BH");
+ isp1301_bh(arg);
+ TRACE_MSG0(REMOVE_TCD, "ZASEVB GPIO INTERRUPT: REENABLE");
+
+ if (zasevb_int_disabled) {
+ zasevb_int_disabled = FALSE;
+ enable_irq(IOMUX_TO_IRQ(PIN_GPIO2));
+ }
+ return 0;
+}
+
+
+
+
+/*!
+ * This function set iomux settings depends on platform and usb mode.
+ * @param otg otg instance
+ * @param usb_mode setting usb mode.
+ *
+ */
+
+int mxc_iomux_gpio_isp1301_set (struct otg_instance *otg, int usb_mode)
+{
+
+ int gpio = 1;
+
+ printk (KERN_INFO"MXC gpio setting for isp1301\n");
+
+ isp1301_mod_init(otg, &zasevb_isp1301_bh);
+
+ TRACE_MSG0(otg->tcd->TAG, "5. IOMUX and GPIO Interrupt Configuration");
+ iomux_config_mux(PIN_GPIO2, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ //Setting interrupt for ISP1301
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ set_irq_type(IOMUX_TO_IRQ(PIN_GPIO2), IRQF_TRIGGER_FALLING);
+ #else
+ set_irq_type(IOMUX_TO_IRQ(PIN_GPIO2), IRQT_FALLING);
+ #endif
+ gpio = request_irq(IOMUX_TO_IRQ(PIN_GPIO2), zasevb_gpio_int_hndlr,
+ 0, "ISP1301", (void *)&ocd_ops);
+ THROW_IF(gpio, error);
+
+
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ UNLESS (gpio) gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int mxc_iomux_gpio_isp1301_reset (void)
+{
+ free_irq(IOMUX_TO_IRQ(PIN_GPIO2), &ocd_ops);
+ return 0;
+}
+
+#endif //CONFIG_OTG_ZASEVB_ISP1301
+
+
+
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+/*! mxc_iomux_gpio_mc1383_set - set mc13783 iomux setting
+ * @param usb_mode
+ */
+int mxc_iomux_gpio_mc13783_set (int usb_mode)
+{
+
+ printk (KERN_INFO"MXC gpio setting for Atlas\n");
+
+ printk(KERN_INFO"IOMUX setting for Argon+\n");
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+
+ return 0;
+}
+
+/*! mxc_iomux_gpio_mc13783_reset -clear mc13783 iomux settings
+ */
+
+int mxc_iomux_gpio_mc13783_reset (void)
+{
+
+ iomux_config_mux(PIN_USB_XRXD, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPOUT, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VPIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_TXENB, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+ iomux_config_mux(PIN_USB_VMIN, OUTPUTCONFIG_FUNC, INPUTCONFIG_FUNC);
+
+ return 0;
+}
+#endif //CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY
+
+
+int mxc_host_gpio (void)
+{
+ iomux_config_mux(PIN_GPIO5, OUTPUTCONFIG_ALT1, INPUTCONFIG_ALT1); // USB1_OC
+ printk(KERN_INFO"%s: NONE/GPIO\n", __FUNCTION__);
+ return 0;
+}
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301)
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_isp1301_reset);
+#endif
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(CONFIG_OTG_ZASEVB_MC13783_PMIC)
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_mc13783_set);
+OTG_EXPORT_SYMBOL(mxc_iomux_gpio_mc13783_reset);
+#endif
+
+OTG_EXPORT_SYMBOL(mxc_host_gpio);
diff --git a/drivers/otg/hardware/otg-dev.c b/drivers/otg/hardware/otg-dev.c
new file mode 100644
index 000000000000..bb42b8062e5b
--- /dev/null
+++ b/drivers/otg/hardware/otg-dev.c
@@ -0,0 +1,484 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/otg-dev.c -- Generic DEV driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/otglib/otg-dev.c|20070918212346|28546
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/otg-dev.c
+ * @brief Generic DEV Driver.
+ *
+ * This supports using a single DEV device to implement various USB OTG
+ * subsidiary drivers.
+ *
+ * The hardware driver is split into drivers for the following.
+ *
+ * Required:
+ *
+ * 1. dev
+ *
+ * One or more of the following:
+ *
+ * 2. ocd
+ * 3. pcd
+ * 4. hcd
+ * 5. tcd
+ *
+ * The dev driver implements a standard dev driver structure which will
+ * be used to register with the linux dev support. During the probe function
+ * the otg_dev_probe() function should be called to create an otg_dev
+ * structure for the device. This is used to keep track of all of the additional
+ * drivers.
+ *
+ * Each of the optional drivers will use otg_dev_register_driver() to let the
+ * dev layer know what optional drivers are available.
+ *
+ * @ingroup OTGDEV
+ * @ingroup LINUXOS
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+//#include <linux/usb.h>
+#include <linux/delay.h>
+
+#include <otg/otg-compat.h>
+
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) */
+
+#include <otg/otg-trace.h>
+#include <otg/usbp-chap9.h>
+#include <otg/otg-api.h>
+#include <otg/otg-dev.h>
+#include <otg/otg-utils.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-ocd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-tcd.h>
+
+
+#define TRACE_VERBOSE 0
+
+/* ********************************************************************************************* */
+static struct otg_dev *otg_devs;
+
+/* ********************************************************************************************* */
+/*!
+ * otg_dev_isr() - interrupt service handler
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+irqreturn_t otg_dev_isr(int irq, void *data, struct pt_regs *r)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+irqreturn_t otg_dev_isr(int irq, void *data)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+{
+ struct otg_interrupt *otg_interrupt; // = data;
+ struct device *device; // = otg_interrupt->device;
+ struct otg_dev *otg_dev; // = dev_get_drvdata(device);
+ struct otg_instance *otg; // = otg_dev->otg_instance;
+ int irqmask; // = 1 << otg_interrupt->irq;
+ int i;
+
+ otg_interrupt = data;
+ device = otg_interrupt->device;
+ otg_dev = dev_get_drvdata(device);
+ irqmask = 1 << otg_interrupt->irq;
+
+ // XXX need to determine what pt_regs *r can tell us, it may
+ // allow sub-driver isr functions to tell if they should do something
+ // so we may want to send that down
+
+ /* XXX spinlock */
+
+ RETURN_IRQ_HANDLED_UNLESS(otg_dev);
+ otg = otg_dev->otg_instance;
+ otg->interrupts++;
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG0(otg_dev->DEV, "---------------------------------------- Start");
+
+ for (i = OTG_DRIVER_TCD; i < OTG_DRIVER_TYPES; i++) {
+ struct otg_dev_driver *otg_dev_driver = otg_dev->otg_device_driver->drivers[i];
+
+ CONTINUE_UNLESS(otg_dev_driver);
+ CONTINUE_UNLESS(otg_dev_driver->isr);
+ CONTINUE_UNLESS(otg_dev_driver->irqs & irqmask);
+ RETURN_IRQ_HANDLED_IF_IRQ_HANDLED (otg_dev_driver->isr(otg_dev, data, irqmask));
+ //TRACE_MSG2(otg_dev->DEV, "not handled by %s %d", otg_dev_driver->name, i);
+ }
+
+ /* XXX spinlock */
+ TRACE_MSG0(otg_dev->DEV, "---------------------------------------- IRQ_NONE ----");
+ return IRQ_NONE;
+}
+
+/* ********************************************************************************************* */
+
+/*!
+ * otg_dev_free_dev
+ */
+void otg_dev_free_dev(struct device *device, struct otg_dev *otg_dev)
+{
+ #if 0
+ int region;
+ unsigned long resource_start;
+ unsigned long resource_len;
+
+ RETURN_UNLESS(otg_dev);
+
+ for (region = 0; region < DEVICE_COUNT_RESOURCE; region++) {
+
+ CONTINUE_UNLESS(otg_dev->pci_regions & (1 << region));
+
+ if (otg_dev->regs[region]) iounmap(otg_dev->regs[region]);
+
+ resource_start = pci_resource_start(pci_dev, region);
+ resource_len = pci_resource_len(pci_dev, region);
+ release_mem_region(resource_start, resource_len);
+ }
+ #endif
+}
+
+/*! otg_free_irqs
+ */
+void otg_free_irqs(struct otg_dev *otg_dev, struct device *device, int num_resources)
+{
+ struct platform_device *platform_device = to_platform_device(device);
+ struct otg_interrupt *otg_interrupts = otg_dev->otg_interrupts;
+ int resource;
+
+ for (resource = 0; resource < MIN(platform_device->num_resources, num_resources); resource++) {
+
+ struct resource *resources = platform_device->resource + resource;
+ struct otg_interrupt *otg_interrupt = otg_interrupts + resource;
+
+
+ CONTINUE_UNLESS(resources->flags == IORESOURCE_IRQ);
+
+ free_irq(resources->start, otg_interrupt);
+ }
+}
+
+
+/*!
+ * otg_dev_probe() - otg dev probe function
+ *
+ */
+int otg_dev_probe (struct device *device, struct otg_device_driver *otg_device_driver, void *privdata)
+{
+ struct platform_device *platform_device = to_platform_device(device);
+
+ struct otg_dev_driver *otg_dev_driver;
+ struct otg_dev *otg_dev = NULL;
+ struct otg_interrupt *otg_interrupts = NULL;
+ int resource = 0;
+
+ u8 latency, limit;
+ int i;
+
+ struct otg_instance *otg = NULL;
+
+
+ /* allocate otg structure */
+ THROW_UNLESS((otg = otg_create()), error);
+
+ /* allocate otg_dev structure and fill in standard fields */
+ THROW_UNLESS((otg_dev = kmalloc(sizeof(struct otg_dev), GFP_KERNEL)), error);
+ THROW_UNLESS((otg_interrupts =
+ kmalloc(sizeof(struct otg_interrupt) * platform_device->num_resources, GFP_KERNEL)), error);
+
+ memset(otg_dev, 0, sizeof(struct otg_dev));
+ otg_dev->num_resources = platform_device->num_resources;
+ otg_dev->otg_interrupts = otg_interrupts;
+ otg_dev->device = device;
+ otg_dev->otg_instance = otg;
+ otg_dev->DEV = otg_trace_obtain_tag(otg, "otg-dev");
+ otg_dev->privdata = privdata;
+
+ otg_dev->otg_device_driver = otg_device_driver;
+ dev_set_drvdata(device, otg_dev);
+ otg_dev_set_drvdata (otg_dev, device);
+
+ for (resource = 0; resource < platform_device->num_resources; resource++) {
+
+ struct resource *resources = platform_device->resource + resource;
+ struct otg_interrupt *otg_interrupt = otg_interrupts + resource;
+
+ CONTINUE_UNLESS(resources->flags == IORESOURCE_IRQ);
+
+ TRACE_MSG2(otg_dev->DEV, "irq: %d start: %d", resource, resources->start);
+
+ /* request irq - use hardware wrapper isr if available */
+
+ otg_interrupt->device = device;
+ otg_interrupt->irq = resource;
+
+ printk(KERN_INFO"%s: REQUEST_IRQ resource: %d start: %d otg_interrupt: %x\n",
+ __FUNCTION__, resource, resources->start, otg_interrupt);
+ THROW_IF(request_irq(resources->start,
+
+ //(otg_dev->otg_device_driver->isr) ?
+ //(otg_dev->otg_device_driver->isr) :
+ otg_dev_isr,
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ SA_SHIRQ,
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+ 0,
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+ otg_dev->otg_device_driver->name,
+ otg_interrupt
+ ), error);
+
+ }
+
+ if (otg_devs) {
+ TRACE_MSG2(otg_dev->DEV, "otg_devs: %x new: %x", otg_devs, otg_dev);
+ otg_dev->next = otg_devs;
+ }
+
+ otg_devs = otg_dev;
+
+ for (i = 0; i < OTG_DRIVER_TYPES; i++) {
+ struct otg_dev_driver *driver = otg_device_driver->drivers[i];
+
+ /* check if we have a driver */
+ CONTINUE_UNLESS(driver);
+
+ /* register sub-driver with otg */
+ switch (driver->id) {
+ case OTG_DRIVER_TCD:
+ otg_dev->tcd_instance = otg_set_tcd_ops(otg_dev->otg_instance, driver->ops);
+ otg_dev->tcd_instance->privdata = otg_dev;
+ break;
+ case OTG_DRIVER_PCD:
+ otg_dev->pcd_instance = otg_set_pcd_ops(otg_dev->otg_instance, driver->ops);
+ otg_dev->pcd_instance->privdata = otg_dev;
+ break;
+ case OTG_DRIVER_HCD:
+ otg_dev->hcd_instance = otg_set_hcd_ops(otg_dev->otg_instance, driver->ops);
+ otg_dev->hcd_instance->privdata = otg_dev;
+ break;
+ case OTG_DRIVER_OCD:
+ otg_dev->ocd_instance = otg_set_ocd_ops(otg_dev->otg_instance, driver->ops);
+ otg_dev->ocd_instance->privdata = otg_dev;
+ break;
+ }
+
+ /* call sub-driver probe() function */
+ CONTINUE_UNLESS(driver->probe);
+ CONTINUE_UNLESS(driver->probe(otg_dev));
+
+ }
+
+ /* start otg state machine */
+ if (otg_device_driver->serial_number && strlen(otg_device_driver->serial_number)) {
+ //TRACE_MSG1(ZAS, "serial_number: %s", otg_device_driver->serial_number);
+ otg_serial_number (otg, otg_device_driver->serial_number);
+ }
+ otg->privdata = otg_dev;
+ otg_init(otg);
+
+ return 0;
+
+ CATCH(error) {
+
+ printk(KERN_INFO"%s: FAILED resource: %d\n", __FUNCTION__, resource);
+
+ if (resource)
+ otg_free_irqs(otg_dev, device, resource);
+
+ if (otg_interrupts)
+ kfree(otg_interrupts);
+
+ dev_set_drvdata(device, NULL);
+
+ otg_dev_free_dev(device, otg_dev);
+
+ if (otg_dev) kfree(otg_dev);
+
+ if (otg)
+ otg_destroy(otg);
+
+ return -EINVAL;
+ }
+}
+
+/*!
+ * otg_dev_remove() - pci remove function
+ */
+void otg_dev_remove (struct device *device , struct otg_device_driver *otg_device_driver)
+{
+ struct platform_device *platform_device = to_platform_device(device);
+
+ struct otg_dev *otg_dev = dev_get_drvdata(device);
+ struct otg_instance *otg = otg_dev->otg_instance;
+ int i;
+
+ /* stop otg state machine */
+ otg_exit(otg);
+
+ /* release interrupts */
+ otg_free_irqs(otg_dev, device, platform_device->num_resources);
+
+ /* de-register sub-drivers from otg */
+ for (i = 0; i < OTG_DRIVER_TYPES; i++) {
+ struct otg_dev_driver *driver = otg_device_driver->drivers[i];
+ CONTINUE_UNLESS(driver);
+ CONTINUE_UNLESS(driver->remove);
+ driver->remove(otg_dev);
+ switch (driver->id) {
+ case OTG_DRIVER_TCD:
+ otg_dev->tcd_instance = otg_set_tcd_ops(otg_dev->otg_instance, NULL);
+ break;
+ case OTG_DRIVER_PCD:
+ otg_dev->pcd_instance = otg_set_pcd_ops(otg_dev->otg_instance, NULL);
+ break;
+ case OTG_DRIVER_HCD:
+ otg_dev->hcd_instance = otg_set_hcd_ops(otg_dev->otg_instance, NULL);
+ break;
+ case OTG_DRIVER_OCD:
+ otg_dev->ocd_instance = otg_set_ocd_ops(otg_dev->otg_instance, NULL);
+ break;
+ }
+
+ }
+
+ /* finally destroy otg instance */
+ otg_destroy(otg);
+
+ /* cleanupj */
+ dev_set_drvdata(device, NULL);
+
+ otg_dev->otg_instance = NULL;
+ kfree(otg_dev->otg_interrupts);
+
+
+ #if 0
+ if (otg_devs == otg_dev) {
+ otg_devs = otg_dev->next;
+ }
+ else {
+ struct otg_dev *link;
+ for (link = otg_devs; link; link = otg_dev->next) {
+ if (link->next == otg_dev) {
+ link->next = otg_dev->next;
+ break;
+ }
+ }
+ }
+ #endif
+
+ otg_dev_free_dev(device, otg_dev);
+ otg_trace_invalidate_tag(otg_dev->DEV);
+ kfree(otg_dev);
+}
+
+
+/* ********************************************************************************************* */
+
+/*!
+ * otg_dev_register_driver() - sub-driver registration function
+ */
+int otg_dev_register_driver(struct otg_device_driver *otg_device_driver, struct otg_dev_driver *otg_dev_driver)
+{
+ struct otg_dev *otg_dev;
+
+ otg_device_driver->drivers[otg_dev_driver->id] = otg_dev_driver;
+
+ #if 0
+ if (otg_dev_driver->probe) {
+ for (otg_dev = otg_devs; otg_dev; otg_dev = otg_dev->next) {
+
+ if (otg_dev_driver->probe(otg_dev, otg_dev_driver->id)) {
+ otg_dev->otg_device_driver->drivers[otg_dev_driver->id] = NULL;
+ return -EINVAL;
+ }
+ return 0;
+ }
+ }
+ #endif
+ return 0;
+}
+
+/*!
+ * otg_dev_unregister_driver() - sub-driver unregistration function
+ */
+void otg_dev_unregister_driver(struct otg_device_driver *otg_device_driver, struct otg_dev_driver *otg_dev_driver)
+{
+ struct otg_dev *otg_dev;
+
+ #if 0
+ if (otg_dev_driver->probe) {
+ for (otg_dev = otg_devs; otg_dev; otg_dev = otg_dev->next) {
+ otg_dev_driver->remove(otg_dev, otg_dev_driver->id);
+ return;
+ }
+ }
+ #endif
+ otg_device_driver->drivers[otg_dev_driver->id] = NULL;
+}
+
+/*!
+ * otg_dev_set_drvdata - set otg_dev data
+ */
+
+void otg_dev_set_drvdata(struct otg_dev *otg_dev, void *data)
+{
+ otg_dev->drvdata = data;
+}
+/*!
+ * otg_dev_get_drvdata - get otg_dev data
+ */
+void * otg_dev_get_drvdata(struct otg_dev *otg_dev)
+{
+ return otg_dev->drvdata;
+}
+
+/* ********************************************************************************************* */
+
+/* ********************************************************************************************* */
+
+
+
+
+OTG_EXPORT_SYMBOL(otg_dev_probe);
+OTG_EXPORT_SYMBOL(otg_dev_remove);
+
+
+OTG_EXPORT_SYMBOL(otg_dev_register_driver);
+OTG_EXPORT_SYMBOL(otg_dev_unregister_driver);
+
+OTG_EXPORT_SYMBOL(otg_dev_set_drvdata);
+OTG_EXPORT_SYMBOL(otg_dev_get_drvdata);
diff --git a/drivers/otg/hardware/pcd.c b/drivers/otg/hardware/pcd.c
new file mode 100644
index 000000000000..56a9dbf6510c
--- /dev/null
+++ b/drivers/otg/hardware/pcd.c
@@ -0,0 +1,1642 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/pcd.c - OTG Peripheral Controller Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/otglib/pcd.c|20070820053759|59156
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/pcd.c
+ * @brief PCD only driver init.
+ * Notes
+ *
+ * 1. The pcd file abstracts much of the UDC complexity as possible and
+ * provides a common implementation that is shared to various extents by the
+ * various pcd drivers.
+ *
+ * @ingroup PCD
+ */
+
+#include <otg/pcd-include.h>
+#include <otg/usbp-func.h>
+
+#define TRACE_VERBOSE 1
+#define TRACE_VERY_VERBOSE 0
+
+/* ******************************************************************************************* */
+/* ******************************************************************************************* */
+
+/*!
+ * @brief pcd_urb_finished_irq() - tell function that an urb has been finished.
+ *
+ * Used by a USB Bus driver to pass a sent urb back to the function
+ * driver via the endpoints finished queue.
+ * @param urb urb to process
+ * @param rc result code
+ *
+ */
+void pcd_urb_finished_irq(struct usbd_urb *urb, int rc)
+{
+ struct usbd_bus_instance *bus = (struct usbd_bus_instance *)urb->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)(bus ? bus->privdata : NULL);
+ struct otg_instance *otg = bus ? bus->otg : NULL;
+ //struct ocd_instance *ocd = otg ? otg->ocd : NULL;
+ //struct ocd_ops *ocd_ops = otg ? otg->ocd_ops : NULL;
+
+ UNLESS(urb && urb->bus) {
+ //TRACE_MSG2(urb->bus->otg->pcd->TAG, "urb: %x bus: %x", urb, urb ? urb->bus : NULL);
+ return;
+ }
+ urb->irq_flags = USBD_URB_FINISHED;
+ urb->status = (usbd_urb_status_t)rc;
+
+ /* fasttrack bus->ep0_urb */
+ if (urb == bus->ep0_urb) {
+ (urb = usbd_first_finished_urb_detached (urb->endpoint, &urb->endpoint->rdy));
+ return;
+ }
+
+
+ TRACE_ELAPSED(pcd->TAG, "PCD 0. DONE", urb->ticks, (u32)urb);
+ otg_get_ocd_info(pcd->otg, &urb->ticks, &urb->framenum);
+
+ if (rc==USBD_URB_CANCELLED){
+ usbd_unlink_urb(urb);
+ usbd_do_urb_callback (urb, urb->status);
+ return;
+ }
+
+ if (urb->flags & USBD_URB_FAST_RETURN) {
+ #ifdef CONFIG_OTG_LATENCY_CHECK
+ otg_tick_t ticks;
+ u16 framenum;
+ otg_tick_t urb_ticks = urb->ticks;
+ otg_get_ocd_info(pcd->otg, &ticks, &framenum);
+ #endif
+ usbd_unlink_urb(urb);
+ usbd_do_urb_callback (urb, urb->status);
+ #ifdef CONFIG_OTG_LATENCY_CHECK
+ /* elapsed time for the callback */
+ TRACE_ELAPSED(pcd->TAG, "PCD 1. CB", ticks, (u32)urb);
+
+ /* elapsed time for all processing since the urb was finished */
+ TRACE_ELAPSED(pcd->TAG, "PCD 2. TOTAL", urb_ticks, (u32)urb);
+ #endif
+ return;
+ }
+
+ //TRACE_MSG1(pcd->TAG, "FINISH %p", urb);
+ TRACE_MSG0(pcd->TAG, "otg_up_work()");
+ otg_up_work(pcd->task);
+}
+
+/* ******************************************************************************************* */
+/*! pcd_rcv_cancelled_irq - cancel current receive urb
+ * Called by UDC driver to cancel the current receive urb.
+ * @param endpoint - endpoint instance pointer
+ */
+void pcd_rcv_cancelled_irq (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *rcv_urb;
+ struct usbd_bus_instance *bus = endpoint->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+
+ TRACE_MSG1(pcd->TAG, "BUS_RCV CANCELLED: %p", (int) endpoint->rcv_urb);
+ RETURN_IF (! (rcv_urb = endpoint->rcv_urb));
+ TRACE_MSG1(pcd->TAG, "rcv_urb: %p", (int)endpoint->rcv_urb);
+ pcd_urb_finished_irq (rcv_urb, USBD_URB_CANCELLED);
+ endpoint->sent = endpoint->last = 0;
+ endpoint->rcv_urb = NULL;
+}
+
+
+/*! pcd_tx_next_irq - get the current or next urb to transmit
+ * Called by UDC driver to get current transmit urb or next available transmit urb.
+ * @param endpoint - endpoint instance pointer
+ * @return urb to transmit
+ */
+struct usbd_urb * pcd_tx_next_irq (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_bus_instance *bus = endpoint->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ struct otg_instance *otg = pcd->otg;
+ if (!endpoint->tx_urb) {
+
+ if ( (endpoint->tx_urb = usbd_first_ready_urb(endpoint, &endpoint->rdy))) {
+ int i;
+ u8 *cp = endpoint->tx_urb->buffer;
+ if (otg)
+ otg_get_ocd_info(otg, &endpoint->tx_urb->ticks, &endpoint->tx_urb->framenum);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(pcd->TAG, "[%2x] NEXT TX: length: %d flags: %x",
+ endpoint->bEndpointAddress,
+ endpoint->tx_urb->actual_length, endpoint->tx_urb->flags);
+
+ if (TRACE_VERY_VERBOSE)
+ for (i = 0; i < endpoint->tx_urb->actual_length; i+= 8)
+ TRACE_MSG8(pcd->TAG, "SENT %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+
+ endpoint->tx_urb->status = USBD_URB_ACTIVE;
+ }
+ }
+ //TRACE_MSG1(endpoint->bus->otg->pcd->TAG, "BUS_TX NEXT TX_URB: %p", (int)endpoint->tx_urb);
+ return endpoint->tx_urb;
+}
+
+/*! pcd_rcv_next_irq - get the current or next urb to receive into
+ * Called by UDC driver to get current receive urb or next available receive urb.
+ * @param endpoint - endpoint instance pointer
+ * @return urb to receive
+ */
+struct usbd_urb * pcd_rcv_next_irq (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_bus_instance *bus = endpoint->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ struct otg_instance *otg = pcd->otg;
+ if (!endpoint->rcv_urb) {
+
+ if ( (endpoint->rcv_urb = usbd_first_ready_urb(endpoint, &endpoint->rdy))) {
+ if (otg)
+ otg_get_ocd_info(otg, &endpoint->tx_urb->ticks, &endpoint->tx_urb->framenum);
+ endpoint->rcv_urb->status = USBD_URB_ACTIVE;
+ }
+
+ }
+ //TRACE_MSG1(endpoint->bus->otg->pcd->TAG, "BUS_RCV_URB: %p", endpoint->rcv_urb);
+ return endpoint->rcv_urb;
+}
+
+/* ******************************************************************************************* */
+/*!
+ * pcd_assign_endpoint()
+ */
+static int
+pcd_assign_endpoint(struct pcd_instance *pcd, u8 index, u8 physicalEndpoint, u32 *used,
+ struct usbd_endpoint_map *endpoint_map, u8 bmAttributes,
+ u16 wMaxPacketSize_fs,
+ u16 wMaxPacketSize_hs,
+ u16 fs_transferSize,
+ u16 hs_transferSize,
+ u8 bInterval)
+{
+ RETURN_EINVAL_IF( *used & (1 << physicalEndpoint) );
+
+ endpoint_map->index = index;
+ endpoint_map->bEndpointAddress[0] = endpoint_map->bEndpointAddress[1] =
+ physicalEndpoint | (USB_DIR_IN & bmAttributes);
+ endpoint_map->physicalEndpoint[0] = endpoint_map->physicalEndpoint[1] =
+ (physicalEndpoint * 2) + ((USB_DIR_IN & bmAttributes) ? 1 : 0);
+ endpoint_map->wMaxPacketSize[0] = wMaxPacketSize_fs;
+ endpoint_map->wMaxPacketSize[1] = wMaxPacketSize_hs;
+ endpoint_map->transferSize[0] = fs_transferSize;
+ endpoint_map->transferSize[1] = hs_transferSize;
+ endpoint_map->bmAttributes[0] = endpoint_map->bmAttributes[1] = bmAttributes;
+ endpoint_map->bInterval[0] = endpoint_map->bInterval[1] = bInterval;
+ *used |= (1 << physicalEndpoint);
+
+ TRACE_MSG6(pcd->TAG, "ASSIGN: index: %02x phys: %02x addr: %02x wMaxPacketSize: %02x:%02x",
+ physicalEndpoint,
+ endpoint_map->physicalEndpoint[0],
+ endpoint_map->bEndpointAddress[0],
+ endpoint_map->bEndpointAddress[1],
+ endpoint_map->wMaxPacketSize[0],
+ endpoint_map->wMaxPacketSize[1]
+ );
+ //printk(KERN_INFO"%s: DDDD\n", __FUNCTION__);
+ return 0;
+}
+
+/*!
+ * pcd_request_endpoints()
+ * @param pcd
+ * @param endpoint_map_array
+ * @param endpointsRequested
+ * @param requestedEndpoints
+ *
+ */
+int
+pcd_request_endpoints(struct pcd_instance *pcd, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested,
+ struct usbd_endpoint_request *requestedEndpoints)
+{
+ //struct usbd_device_description *device_description;
+ int i, in, out;
+ u32 in_used = 0;
+ u32 out_used = 0;
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ TRACE_MSG1(pcd->TAG, "REQUEST ENDPOINTS: %d", endpointsRequested);
+ for (in = out = 1, i = 0; i < endpointsRequested; i++) {
+ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i;
+ u8 index = requestedEndpoints[i].index;
+ u8 bInterval = requestedEndpoints[i].bInterval;
+ u8 bmAttributes = requestedEndpoints[i].bmAttributes;
+ u16 fs_transferSize = requestedEndpoints[i].fs_requestedTransferSize;
+ u16 hs_transferSize = requestedEndpoints[i].hs_requestedTransferSize;
+ TRACE_MSG5(pcd->TAG, "REQUEST ENDPOINTS[%2x] index: %02x bm: %02x trans: %04x:%04x",
+ i, index, bmAttributes, fs_transferSize, hs_transferSize);
+
+
+ switch(bmAttributes) {
+
+ case USB_DIR_IN | USB_ENDPOINT_BULK:
+ RETURN_EINVAL_UNLESS(in < usbd_pcd_ops.max_endpoints);
+ CONTINUE_IF(!pcd_assign_endpoint(pcd, index, in++, &in_used, endpoint_map, bmAttributes,
+ 0x40, 0x200, fs_transferSize, hs_transferSize, bInterval));
+ THROW(error);
+
+ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT:
+ case USB_DIR_IN | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT:
+ RETURN_EINVAL_UNLESS(in < usbd_pcd_ops.max_endpoints);
+ CONTINUE_IF(!pcd_assign_endpoint(pcd, index, in++, &in_used, endpoint_map, bmAttributes,
+ 0x40, 0x40, fs_transferSize, hs_transferSize, bInterval));
+ THROW(error);
+
+ case USB_DIR_OUT | USB_ENDPOINT_BULK:
+ RETURN_EINVAL_UNLESS(out < usbd_pcd_ops.max_endpoints);
+ CONTINUE_IF(!pcd_assign_endpoint(pcd, index, out++, &out_used, endpoint_map, bmAttributes,
+ 0x40, 0x200, fs_transferSize, hs_transferSize, bInterval));
+ THROW(error);
+ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT:
+ case USB_DIR_OUT | USB_ENDPOINT_INTERRUPT | USB_ENDPOINT_OPT:
+ RETURN_EINVAL_UNLESS(out < usbd_pcd_ops.max_endpoints);
+ CONTINUE_IF(!pcd_assign_endpoint(pcd, index, out++, &out_used, endpoint_map, bmAttributes,
+ 0x40, 0x40, fs_transferSize, hs_transferSize, bInterval));
+ //CONTINUE_IF(!pcd_assign_endpoint(pcd, index, in++, &in_used, endpoint_map, bmAttributes,
+ // 0x40, fs_transferSize, hs_transferSize, bInterval));
+ THROW(error);
+
+ case USB_ENDPOINT_CONTROL:
+ case USB_DIR_IN | USB_ENDPOINT_ISOCHRONOUS:
+ case USB_DIR_OUT | USB_ENDPOINT_ISOCHRONOUS:
+ THROW(error);
+ }
+ THROW(error);
+ CATCH(error) {
+ TRACE_MSG4(pcd->TAG, "FAILED num: %d bmAttributes: %02x transferSize: %04x:%04x",
+ i, bmAttributes, fs_transferSize, hs_transferSize);
+
+ //printk(KERN_ERR"%s: FAILED num: %d bmAttributes: %02x transferSize: %02x\n",
+ // __FUNCTION__, i, bmAttributes, transferSize);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/* ******************************************************************************************* */
+/*! bus_request_endpoints -
+ * @param bus
+ * @param endpoint_map_array
+ * @param endpointsRequested
+ * @param requestedEndpoints
+ * @return 0 on finish, or -EINVAL
+ */
+int bus_request_endpoints(struct usbd_bus_instance *bus, struct usbd_endpoint_map *endpoint_map_array, int endpointsRequested,
+ struct usbd_endpoint_request *requestedEndpoints)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ int i;
+ TRACE_MSG0(pcd->TAG, "BUS_REQUEST ENDPOINTS:");
+ if (usbd_pcd_ops.request_endpoints(pcd, endpoint_map_array, endpointsRequested, requestedEndpoints)) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ for (i = 0; i < endpointsRequested; i++) {
+ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + i;
+ //TRACE_MSG8(pcd->TAG, "address: %02x:%02x physical: %02x:%02x request: %02x:%02x size: %04x:%04x",
+ // endpoint_map->bEndpointAddress[0], endpoint_map->bEndpointAddress[1],
+ // endpoint_map->physicalEndpoint[0], endpoint_map->physicalEndpoint[1],
+ // endpoint_map->bmAttributes[0], endpoint_map->bmAttributes[1],
+ // endpoint_map->wMaxPacketSize[0], endpoint_map->wMaxPacketSize[1]
+ // );
+ TRACE_MSG8(pcd->TAG, "REQUEST[%02d:%02d] endpoint_map: %x address: %04x physical: %04x request: %04x size: %04x:%04x",
+ i, endpointsRequested,
+ endpoint_map,
+ endpoint_map->bEndpointAddress[0] << 8 | endpoint_map->bEndpointAddress[1],
+ endpoint_map->physicalEndpoint[0] << 8 | endpoint_map->physicalEndpoint[1],
+ endpoint_map->bmAttributes[0] << 8 | endpoint_map->bmAttributes[1],
+ endpoint_map->wMaxPacketSize[0],
+ endpoint_map->wMaxPacketSize[1]
+ );
+ }
+ TRACE_MSG0(pcd->TAG, "BUS_REQUEST ENDPOINTS: finished");
+ return 0;
+}
+
+/*! bus_set_endpoints - initialize pcd instance endpoints array
+ * @param bus - usbd_bus_instance pointer
+ * @param endpointsRequested - number of requested endpoints
+ * @param endpoint_map_array - usbd_endpoint_map pointer
+ */
+int bus_set_endpoints(struct usbd_bus_instance *bus, int endpointsRequested, struct usbd_endpoint_map *endpoint_map_array)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ TRACE_MSG5(pcd->TAG, "SET ENDPOINTS bus: %p endpointsRequested: %d endpoint_map_array: %p pcd: %x pcd->otg: %x",
+ bus, endpointsRequested, endpoint_map_array, pcd, pcd ? pcd->otg : NULL);
+ // XXX do we need to set bEndpointAddress
+ return usbd_pcd_ops.set_endpoints ? usbd_pcd_ops.set_endpoints(pcd, endpointsRequested, endpoint_map_array) : 0;
+}
+
+/*! bus_set_configuration -
+ * @param bus - bus instance pointer
+ * @param config
+ */
+int bus_set_configuration(struct usbd_bus_instance *bus, int config)
+{
+ pcd_bus_event_handler_irq (bus, config? DEVICE_CONFIGURED : DEVICE_DE_CONFIGURED, 0);
+ return 0;
+}
+
+/*! bus_set_address - called to set device address
+ * @param bus - bus instance pointer
+ * @param address - address to set
+ */
+int bus_set_address(struct usbd_bus_instance *bus, int address)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ pcd->new_address = address;
+ pcd_bus_event_handler_irq (bus, DEVICE_ADDRESS_ASSIGNED, address);
+ if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, address);
+ return 0;
+}
+
+/*! bus_set_speed - called to set bus high speed capability
+ * @param bus - bus instance pointer
+ * @param hs - high speed capability
+ */
+void bus_set_speed(struct usbd_bus_instance *bus, int hs)
+{
+ bus->high_speed = hs;
+}
+
+/* ******************************************************************************************* */
+
+/*! pcd_search_endpoint_index - find endpoint map index given endpoint address
+ * @param bus - usbd_bus_instance pointer
+ * @param bEndpointAddress - endbpoint address
+ * @return found endpoint index
+ */
+int pcd_search_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress)
+{
+ int i;
+ for (i = 0; i < bus->endpoints; i++)
+ BREAK_IF (bus->endpoint_array[i].bEndpointAddress[bus->high_speed] == bEndpointAddress);
+ return i;
+}
+
+/*! pcd_search_endpoint - find endpoint given endpoint address
+ * @param bus - bus instance pointer
+ * @param bEndpointAddress - endpoint address
+ * @return found endpoint instance pointer
+ */
+struct usbd_endpoint_instance * pcd_search_endpoint(struct usbd_bus_instance *bus, int bEndpointAddress)
+{
+ int i = pcd_search_endpoint_index(bus, bEndpointAddress);
+ //TRACE_MSG2(pcd->TAG, "BUS_SEARCH ENDPOINT: addr: %02x index: %d", bEndpointAddress, i);
+ RETURN_NULL_UNLESS(i < bus->endpoints);
+ return &(bus->endpoint_array[i]);
+}
+
+/*! bus_endpoint_halted - check if endpoint halted
+ * Used by the USB Device Core to check endpoint halt status.
+ *
+ * Return actual halted status if available or'd with endpoint->feature_setting set value.
+ * Assume not halted if udc driver does not provide an endpoint halted function.
+ * @param bus - bus instance pointer
+ * @param bEndpointAddress - endpoint to check halt status
+ * @return check result
+ */
+int bus_endpoint_halted (struct usbd_bus_instance *bus, int bEndpointAddress)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress);
+ struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress);
+ int halted;
+
+ TRACE_MSG1(pcd->TAG, "BUS_ENDPOINT HALTED: endpoint: %p", (int) endpoint);
+ RETURN_EINVAL_UNLESS(endpoint);
+
+ /* return true IFF endpoint was previously halted by host OR if hardware
+ * has been stalled.
+ */
+
+ halted = usbd_pcd_ops.endpoint_halted ? usbd_pcd_ops.endpoint_halted(pcd, endpoint) : 0;
+ TRACE_MSG2(pcd->TAG, "BUS_ENDPOINT STALLED %d FEATURE: %08x",
+ halted, bus->endpoint_array[endpoint_index].feature_setting);
+
+ return halted || (endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)? 1 : 0);
+}
+
+/*! bus_halt_endpoint - handle set/clear feature requests
+ * Used by the USB Device Core to set/clear endpoint halt status.
+ *
+ * We assume that if the udc driver does not implement anything then
+ * we should just return zero for ok.
+ *
+ * XXX should do endpoint instance as arg
+ * @param bus - bus instance pointer
+ * @param bEndpointAddress - endpoint address
+ * @param flag - set/clear flag
+ *
+ */
+int bus_halt_endpoint (struct usbd_bus_instance *bus, int bEndpointAddress, int flag)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ //int endpoint_index = pcd_search_endpoint_index(bus, bEndpointAddress);
+ struct usbd_endpoint_instance *endpoint = pcd_search_endpoint(bus, bEndpointAddress);
+
+ TRACE_MSG3(pcd->TAG, "BUS_ENDPOINT HALT: endpoint: %p bEndpointAddress: %02x flag: %d",
+ endpoint, bEndpointAddress, flag);
+
+ RETURN_EINVAL_UNLESS(endpoint);
+
+ endpoint->feature_setting = flag ?
+ (endpoint->feature_setting | FEATURE(USB_ENDPOINT_HALT)) :
+ (endpoint->feature_setting & ~FEATURE(USB_ENDPOINT_HALT));
+
+ if (flag)
+ usbd_flush_endpoint(endpoint);
+
+ return usbd_pcd_ops.halt_endpoint ? usbd_pcd_ops.halt_endpoint(pcd, endpoint, flag) : 0;
+}
+
+/*! bus_device_feature - handle set/clear feature requests
+ * Used by the USB Device Core to set/clear device feature requests
+ *
+ * We assume that if the udc driver does not implement anything then
+ * we should just return -EINVAL.
+ *
+ * @param bus - bus instance pointer
+ * @param selector - selector
+ * @param flag - set/clear flag
+ *
+ */
+int bus_device_feature (struct usbd_bus_instance *bus, int selector, int flag)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+
+ TRACE_MSG2(pcd->TAG, "BUS_DEVICE_FEATURE selector: %d flag: %d", selector, flag);
+
+ return usbd_pcd_ops.device_feature ? usbd_pcd_ops.device_feature(pcd, selector, flag) : -EINVAL;
+}
+
+/*! pcd_disable_endpoints - disable udc and all endpoints
+ * @param bus - bus instance pointer
+ */
+static void pcd_disable_endpoints (struct usbd_bus_instance *bus)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ int epn;
+ TRACE_MSG0(pcd->TAG, "--");
+ RETURN_IF (!bus || !bus->endpoint_array);
+ for (epn = 0; epn < usbd_pcd_ops.max_endpoints; epn++) {
+ struct usbd_endpoint_instance *endpoint = (bus->endpoint_array + epn);
+ TRACE_MSG2(pcd->TAG, "epn: %d endpoint: %p", epn, endpoint);
+ CONTINUE_IF (!endpoint);
+ usbd_flush_endpoint (endpoint);
+ }
+}
+
+
+/*! bus_event_handler - handle generic bus event
+ * Called by usb core layer to inform bus of an event.
+ * @param bus - bus instance pointer
+ * @param event - usbd_device_event
+ * @param data -
+ */
+int bus_event_handler (struct usbd_bus_instance *bus, usbd_device_event_t event, int data)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ int epn;
+ //struct usbd_endpoint_map *endpoint_map_array = bus->function_instance->endpoint_map_array;
+ int hs = bus->high_speed;
+
+ TRACE_MSG1(pcd->TAG, "BUS_EVENT %d", event);
+ switch (event) {
+ case DEVICE_UNKNOWN:
+ break;
+
+ case DEVICE_INIT:
+ TRACE_MSG0(pcd->TAG, "EVENT INIT");
+ break;
+
+ case DEVICE_CREATE:
+ TRACE_MSG0(pcd->TAG, "EVENT CREATE");
+ pcd_disable_endpoints (bus);
+ if (usbd_pcd_ops.start) usbd_pcd_ops.start (pcd);
+ break;
+
+ case DEVICE_HUB_CONFIGURED:
+ TRACE_MSG0(pcd->TAG, "EVENT HUB_CONFIGURED");
+ //bi_connect (bus, NULL);
+ break;
+
+ case DEVICE_RESET:
+ TRACE_MSG0(pcd->TAG, "EVENT RESET");
+
+ if (usbd_pcd_ops.set_address) usbd_pcd_ops.set_address (pcd, 0);
+ if (usbd_pcd_ops.reset_ep) usbd_pcd_ops.reset_ep (pcd, 0);
+
+ pcd_disable_endpoints (bus);
+ otg_queue_event(pcd->otg, (otg_current_t)(BUS_RESET | BUS_SUSPENDED_), pcd->TAG, "DEVICE_RESET");
+
+ break;
+
+ case DEVICE_ADDRESS_ASSIGNED:
+ TRACE_MSG1(pcd->TAG, "EVENT ADDRESSED: %d", data);
+ otg_queue_event(pcd->otg, ADDRESSED, pcd->TAG, "ADDRESSED");
+
+ break;
+
+ case DEVICE_CONFIGURED:
+ TRACE_MSG0(pcd->TAG, "EVENT CONFIGURED");
+ otg_queue_event(pcd->otg, CONFIGURED, pcd->TAG, "CONFIGURED");
+ // iterate across the physical endpoint instance array to enable the endpoints
+ if (usbd_pcd_ops.setup_ep)
+ for (epn = 1; epn < bus->endpoints; epn++)
+ if (bus->endpoint_array[epn].physicalEndpoint[hs])
+ usbd_pcd_ops.setup_ep (pcd, epn, bus->endpoint_array + epn);
+ break;
+
+ case DEVICE_DE_CONFIGURED:
+ TRACE_MSG0(pcd->TAG, "EVENT DE-CONFIGURED");
+ otg_queue_event(pcd->otg, CONFIGURED_, pcd->TAG, "DE-CONFIGURED");
+ break;
+
+ case DEVICE_SET_INTERFACE:
+ TRACE_MSG0(pcd->TAG, "EVENT SET INTERFACE");
+ break;
+
+ case DEVICE_SET_FEATURE:
+ TRACE_MSG0(pcd->TAG, "EVENT SET FEATURE");
+ break;
+
+ case DEVICE_CLEAR_FEATURE:
+ TRACE_MSG0(pcd->TAG, "EVENT CLEAR FEATURE");
+ break;
+
+ case DEVICE_BUS_INACTIVE:
+ TRACE_MSG0(pcd->TAG, "EVENT INACTIVE");
+ TRACE_MSG1(pcd->TAG, "EVENT INACTIVE: state: %x", bus->device_state);
+ otg_queue_event(pcd->otg, BUS_SUSPENDED, pcd->TAG, "DEVICE_BUS_INACTIVE");
+ break;
+
+ case DEVICE_BUS_ACTIVITY:
+ TRACE_MSG0(pcd->TAG, "EVENT ACTIVITY");
+ otg_queue_event(pcd->otg, BUS_SUSPENDED_, pcd->TAG, "DEVICE_BUS_ACTIVITY");
+ break;
+
+ case DEVICE_POWER_INTERRUPTION:
+ TRACE_MSG0(pcd->TAG, "POWER INTERRUPTION");
+ break;
+
+ case DEVICE_HUB_RESET:
+ TRACE_MSG0(pcd->TAG, "HUB RESET");
+ break;
+
+ case DEVICE_DESTROY:
+ //printk(KERN_INFO"%s: DESTROY\n", __FUNCTION__);
+ TRACE_MSG0(pcd->TAG, "DEVICE DESTROY");
+ //bi_disconnect (bus, NULL);
+ pcd_disable_endpoints (bus);
+ if (usbd_pcd_ops.stop) usbd_pcd_ops.stop (pcd);
+ break;
+
+ case DEVICE_CLOSE:
+ TRACE_MSG0(pcd->TAG, "DEVICE CLOSE");
+ break;
+ default:
+ TRACE_MSG1(pcd->TAG, "DEVICE UNKNOWN: %d", event);
+ break;
+ }
+ TRACE_MSG1(pcd->TAG, "BUS_EVENT %d finished", event);
+ //TRACE_MSG2(pcd->TAG, "EVENT bNumInterfaces: %x alternates: %x", bus->bNumInterfaces, bus->alternates);
+ return 0;
+}
+
+
+/*! bus_start_endpoint_in - called to send urb
+ * @param bus - bus instance pointer
+ * @param endpoint - endpoint pointer
+ * @return 0 on finish
+ */
+int bus_start_endpoint_in (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+
+ RETURN_ZERO_IF (!endpoint);
+ pcd->otg->interrupts++;
+
+ /* call pcd_start_endpoint_in IFF we didn't previously have a tx urb */
+ otg_pthread_mutex_lock(&pcd->mutex);
+ if (!endpoint->tx_urb && pcd_tx_next_irq (endpoint))
+ usbd_pcd_ops.start_endpoint_in (pcd, endpoint);
+ otg_pthread_mutex_unlock(&pcd->mutex);
+ return 0;
+}
+
+/*! bus_start_endpoint_out -called to receive urb
+ * @param bus - bus instance pointer
+ * @param endpoint - endpoint instance pointer
+ * @return 0 on finish
+ */
+int bus_start_endpoint_out (struct usbd_bus_instance *bus, struct usbd_endpoint_instance *endpoint)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ RETURN_ZERO_IF (!endpoint);
+ pcd->otg->interrupts++;
+
+ /* call pcd_start_endpoint_OUT IFF we didn't previously have a rcv urb */
+ otg_pthread_mutex_lock(&pcd->mutex);
+ if (!endpoint->rcv_urb && pcd_rcv_next_irq (endpoint))
+ usbd_pcd_ops.start_endpoint_out (pcd, endpoint);
+ otg_pthread_mutex_unlock(&pcd->mutex);
+ return 0;
+}
+
+/*! bus_cancel_urb_irq - cancel sending an urb
+ * Used by the USB Device Core to cancel an urb.
+ * @param urb - urb to cancel sending operation
+ */
+int bus_cancel_urb_irq (struct usbd_urb *urb)
+{
+ struct usbd_bus_instance *bus = urb->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ struct usbd_endpoint_instance *endpoint;
+ RETURN_EINVAL_IF (!urb);
+ TRACE_MSG1(pcd->TAG, "BUS_CANCEL URB: %x", (int)urb);
+ endpoint = urb->endpoint;
+ switch (urb->endpoint->bEndpointAddress[urb->bus->high_speed] & USB_ENDPOINT_DIR_MASK) {
+ case USB_DIR_IN:
+ // is this the active urb?
+ if (urb->endpoint->tx_urb == urb) {
+ urb->endpoint->tx_urb = NULL;
+ //TRACE_MSG1(pcd->TAG, "CANCEL IN URB: %02x", urb->endpoint->bEndpointAddress[urb->bus->high_speed]);
+ if (usbd_pcd_ops.cancel_in_irq) usbd_pcd_ops.cancel_in_irq (pcd, urb);
+ }
+ pcd_urb_finished_irq (urb, USBD_URB_CANCELLED);
+ break;
+
+ case USB_DIR_OUT:
+ // is this the active urb?
+ if (urb->endpoint->rcv_urb == urb) {
+ urb->endpoint->rcv_urb = NULL;
+ //TRACE_MSG1(pcd->TAG, "CANCEL OUT URB: %02x", urb->endpoint->bEndpointAddress[urb->bus->high_speed]);
+ if (usbd_pcd_ops.cancel_out_irq) usbd_pcd_ops.cancel_out_irq (pcd, urb);
+ }
+ //TRACE_MSG0(pcd->TAG, "CANCEL RECV URB");
+ pcd_urb_finished_irq (urb, USBD_URB_CANCELLED);
+ break;
+ }
+ endpoint->last = endpoint->sent = 0;
+ return 0;
+}
+
+/*! pcd_rcv_finished_irq--
+ * @param endpoint - endpoint instance pointer
+ * @param len -
+ * @param urb_bad
+ */
+struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad)
+{
+ struct usbd_urb *rcv_urb;
+
+ // if we had an urb then update actual_length, dispatch if neccessary
+ if (otg_likely ( (int) (rcv_urb = endpoint->rcv_urb))) {
+ struct usbd_bus_instance *bus = rcv_urb->bus;
+ struct pcd_instance *pcd;
+ pcd = (struct pcd_instance *)bus->privdata;
+
+ //TRACE_MSG5(pcd->TAG, "BUS_RCV COMPLETE: finished buffer: %x actual: %d len: %d bad: %d: status: %d",
+ // rcv_urb->buffer, rcv_urb->actual_length, len, urb_bad, rcv_urb->status);
+
+ if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK)) {
+
+#if 0
+ int i;
+ u8 *cp = rcv_urb->buffer;
+ for (i = 0; i < rcv_urb->actual_length + len; i+= 8) {
+
+ TRACE_MSG8(pcd->TAG, "RECV %02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+#endif
+ // increment the received data size
+ rcv_urb->actual_length += len;
+
+ if (rcv_urb->actual_length && !len)
+ rcv_urb->flags |= USBD_URB_SENDZLP;
+
+ endpoint->rcv_urb = NULL;
+ rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum (bus->otg) : 0;
+ TRACE_MSG2(pcd->TAG, "BUS_RCV COMPLETE: urb: %p framenum: %x", rcv_urb, (int) rcv_urb->framenum);
+ pcd_urb_finished_irq (rcv_urb, USBD_URB_OK);
+ rcv_urb = NULL;
+ }
+ else {
+ rcv_urb->actual_length = 0;
+ //endpoint->rcv_error = 1;
+ }
+ }
+
+ // if we don't have an urb see if we can get one
+ return pcd_rcv_next_irq (endpoint);
+}
+
+/*! pcd_rcv_complete_irq
+ */
+struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad)
+{
+ struct usbd_urb *rcv_urb;
+
+ /* if we had an urb then update actual_length, dispatch if neccessary */
+ if (otg_likely ( (int) (rcv_urb = endpoint->rcv_urb))) {
+ struct usbd_bus_instance *bus = rcv_urb->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+
+ #if 0
+ TRACE_MSG5(pcd->TAG, "BUS_RCV COMPLETE: buffer: %d actual: %d len: %d bad: %d: status: %d",
+ rcv_urb->buffer_length, rcv_urb->actual_length, len, urb_bad, rcv_urb->status);
+ #endif
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(pcd->TAG, "[%2x] alloc: %d buffer: %d actual: %d packet: %d transfer: %d len: %d",
+ endpoint->bEndpointAddress,
+ rcv_urb->alloc_length, rcv_urb->buffer_length, rcv_urb->actual_length,
+ endpoint->wMaxPacketSize[bus->high_speed], endpoint->rcv_transferSize, len);
+
+
+ // check the urb is ok, are we adding data less than the packetsize
+ if (!urb_bad && !endpoint->rcv_error && (rcv_urb->bus->status == USBD_OK)
+ /*&& (len <= endpoint->wMaxPacketSize)*/)
+ {
+
+ /* increment the received data size */
+ rcv_urb->actual_length += len;
+
+ /* if the current received data is short (less than full packetsize) which
+ * indicates the end of the bulk transfer, we have received the maximum
+ * transfersize, or if we do not have enough room to receive another packet
+ * then pass this data up to the function driver
+ */
+
+ if (
+ (
+ ((rcv_urb->actual_length % endpoint->wMaxPacketSize[bus->high_speed])) ||
+ (rcv_urb->actual_length >= rcv_urb->buffer_length) ||
+ (rcv_urb->actual_length + endpoint->wMaxPacketSize[bus->high_speed] >
+ rcv_urb->alloc_length)))
+ {
+ //TRACE_MSG2(pcd->TAG, "BUS_RCV COMPLETE: finished length: %d framenum: %x",
+ // rcv_urb->actual_length, (int) rcv_urb->framenum);
+
+ /* N.B. we don't reset endpoint->rcv_urb until finish urb is run
+ */
+ rcv_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum (bus->otg) : 0;
+ pcd_urb_finished_irq (rcv_urb, USBD_URB_OK);
+ endpoint->rcv_urb = rcv_urb = NULL;
+ endpoint->last = 0;
+ }
+ }
+ else {
+ rcv_urb->actual_length = 0;
+ }
+ }
+
+ /* if we don't have an urb see if we can get one */
+ return pcd_rcv_next_irq (endpoint);
+}
+
+/*! pcd_tx_complete_irq
+ */
+struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart)
+{
+ struct usbd_urb *tx_urb;
+
+ //TRACE_MSG2(pcd->TAG, "tx_urb: %x restart: %d", endpoint->tx_urb, restart);
+
+ // if we have a tx_urb advance or reset, finish if complete
+
+
+ if ( (tx_urb = endpoint->tx_urb)) {
+
+ struct usbd_bus_instance *bus = tx_urb->bus;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+
+ //printk(KERN_INFO"%s: tx_urb: %x endpoint->tx_urb: %x\n", __FUNCTION__, tx_urb, endpoint->tx_urb);
+
+ //printk(KERN_INFO"%s: finishing tx_urb\n", __FUNCTION__);
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG6(pcd->TAG, "[%2x] BUS_TX CURRENT TX_URB: %p actual: %d sent: %d last: %d: flags: %x",
+ endpoint->bEndpointAddress,
+ (int)endpoint->tx_urb, tx_urb->actual_length, endpoint->sent,
+ endpoint->last, tx_urb->flags);
+
+ if (otg_likely (!restart)) {
+ endpoint->sent += endpoint->last;
+ }
+ else {
+ TRACE_MSG1(pcd->TAG, "[%2x] RESTARTING", endpoint->bEndpointAddress);
+ }
+ endpoint->last = 0;
+
+ if ( (endpoint->sent >= tx_urb->actual_length) && ! (tx_urb->flags & USBD_URB_SENDZLP) ) {
+ endpoint->tx_urb = NULL;
+ endpoint->last = endpoint->sent = 0;
+ tx_urb->framenum = pcd_ops.framenum ? pcd_ops.framenum (bus->otg) : 0;
+ if (TRACE_VERBOSE)
+ TRACE_MSG3(pcd->TAG, "[%2x] BUS_TX COMPLETE: finished tx_urb: %p framenum: %x",
+ endpoint->bEndpointAddress,
+ (int)tx_urb, (int)tx_urb->framenum);
+ pcd_urb_finished_irq (tx_urb, USBD_URB_OK);
+ }
+ }
+ return pcd_tx_next_irq (endpoint);
+}
+
+/* ******************************************************************************************* */
+
+/*! pcd_disable - Stop using the USB Device Controller
+ * Stop using the USB Device Controller.
+ * Shutdown and free dma channels, de-register the interrupt handler.
+ */
+void pcd_disable (struct usbd_bus_instance *bus)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ int hs = bus->high_speed;
+ int epn;
+
+ TRACE_MSG0(pcd->TAG, "BUS_UDC DISABLE");
+ if (usbd_pcd_ops.disable_ep) usbd_pcd_ops.disable_ep (pcd, 0, bus->endpoint_array);
+
+ pcd_disable_endpoints (bus);
+
+ if (usbd_pcd_ops.disable_ep)
+ for (epn = 1; epn < bus->endpoints; epn++)
+ if (bus->endpoint_array[epn].physicalEndpoint[hs])
+ usbd_pcd_ops.disable_ep (pcd, epn, bus->endpoint_array + epn);
+
+ if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd);
+}
+
+/* ************************************************************************************* */
+/*! bus_framenum
+ */
+int bus_framenum (struct usbd_bus_instance *bus)
+{
+ //struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ return (pcd_ops.framenum) ? pcd_ops.framenum (bus->otg) : 0;
+}
+
+#if 0
+/*! bus_interrupts
+ */
+u64 bus_interrupts (void)
+{
+ return pcd_instance->otg->interrupts;
+}
+#endif
+
+/*! pcd_ticks
+ */
+otg_tick_t pcd_ticks (void)
+{
+ return usbd_pcd_ops.ticks ? usbd_pcd_ops.ticks (NULL) : 0;
+}
+
+/*! pcd_elapsed
+ */
+otg_tick_t pcd_elapsed (otg_tick_t *t1, otg_tick_t *t2)
+{
+ return usbd_pcd_ops.elapsed ? (usbd_pcd_ops.elapsed (t1, t2)) : 0;
+}
+
+/*! pcd_recv_setup_emulate_irq - emulate a device request
+ */
+int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *bus, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ struct usbd_device_request request;
+
+ //printk(KERN_INFO"bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x\n",
+ // bmRequestType, bRequest, wValue, wIndex);
+ TRACE_MSG4(pcd->TAG, "bmRequestType: %02x bRequest: %02x wValue: %04x wIndex: %04x",
+ bmRequestType, bRequest, wValue, wIndex);
+ request.bmRequestType = bmRequestType;
+ request.bRequest = bRequest;
+ request.wValue = cpu_to_le16(wValue);
+ request.wIndex = cpu_to_le16(wIndex);
+ request.wLength = cpu_to_le16(wLength);
+ return pcd_recv_setup_irq(pcd, &request);
+}
+
+/*! pcd_check_device_feature - verify that feature is set or clear
+ * Check current feature setting and emulate SETUP Request to set or clear
+ * if required.
+ * @param bus -
+ * @param feature -
+ * @param flag
+ */
+void pcd_check_device_feature(struct usbd_bus_instance *bus, int feature, int flag)
+{
+ int status = bus->device_feature_settings & (1 << feature);
+ //TRACE_MSG2(pcd->TAG, "BUS_CHECK FEATURE: feature: %x flag: %x", feature, flag);
+ if (!status && flag)
+ pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_SET_FEATURE, feature, 0, 0);
+ else if (status && !flag)
+ pcd_recv_setup_emulate_irq(bus, USB_REQ_HOST2DEVICE, USB_REQ_CLEAR_FEATURE, feature, 0, 0);
+ //TRACE_MSG1(pcd->TAG, "BUS_CHECK FEATURE: features; %08x", bus->device_feature_settings);
+}
+
+/* ******************************************************************************************* */
+#if defined(CONFIG_OTG_NOC99)
+struct usbd_bus_operations bus_ops;
+/*!
+ * pcd_global_init - initialize pcd related bus operations
+ */
+void pcd_global_init(void)
+{
+ ZERO(bus_ops);
+ bus_ops.start_endpoint_in = bus_start_endpoint_in;
+ bus_ops.start_endpoint_out = bus_start_endpoint_out;
+ bus_ops.cancel_urb_irq = bus_cancel_urb_irq;
+ bus_ops.endpoint_halted = bus_endpoint_halted;
+ bus_ops.halt_endpoint = bus_halt_endpoint;
+ bus_ops.device_feature = bus_device_feature;
+ bus_ops.set_configuration = bus_set_configuration;
+ bus_ops.set_address = bus_set_address;
+ bus_ops.event_handler = bus_event_handler;
+ bus_ops.request_endpoints = bus_request_endpoints;
+ bus_ops.set_endpoints = bus_set_endpoints;
+ bus_ops.framenum = bus_framenum;
+ //bus_ops.ticks = bus_ticks;
+ //bus_ops.elapsed = bus_elapsed;
+ //bus_ops.interrupts = bus_interrupts;
+ //bus_ops.device_feature = bus_device_feature;
+}
+
+#else /* defined(CONFIG_OTG_NOC99) */
+struct usbd_bus_operations bus_ops = {
+ .start_endpoint_in = bus_start_endpoint_in,
+ .start_endpoint_out = bus_start_endpoint_out,
+ .cancel_urb_irq = bus_cancel_urb_irq,
+ .endpoint_halted = bus_endpoint_halted,
+ .halt_endpoint = bus_halt_endpoint,
+ .device_feature = bus_device_feature,
+ .set_configuration = bus_set_configuration,
+ .set_address = bus_set_address,
+ .event_handler = bus_event_handler,
+ .request_endpoints = bus_request_endpoints,
+ .set_endpoints = bus_set_endpoints,
+ .framenum = bus_framenum,
+ //.ticks = bus_ticks,
+ //.elapsed = bus_elapsed,
+ //.interrupts = bus_interrupts,
+ //.device_feature = bus_device_feature,
+};
+#endif /* defined(CONFIG_OTG_NOC99) */
+
+
+struct usbd_bus_driver bus_driver = {
+bops: &bus_ops,
+};
+
+
+/* ******************************************************************************************* */
+
+/*!
+ * Bus Event Handling:
+ * pcd_bus_event_handler()
+ *
+ */
+static usbd_device_state_t event_states[DEVICE_CLOSE] = {
+ STATE_UNKNOWN, STATE_INIT, STATE_ATTACHED, STATE_POWERED,
+ STATE_DEFAULT, STATE_ADDRESSED, STATE_CONFIGURED, STATE_UNKNOWN,
+ STATE_UNKNOWN, STATE_UNKNOWN, STATE_ADDRESSED, STATE_SUSPENDED,
+ STATE_UNKNOWN, STATE_POWERED, STATE_ATTACHED,
+};
+
+static usbd_device_status_t event_status[DEVICE_CLOSE+1] = {
+ USBD_UNKNOWN, // DEVICE_UNKNOWN
+ USBD_OPENING, // DEVICE_INIT
+ USBD_OPENING, // DEVICE_CREATE
+ USBD_OPENING, // DEVICE_HUB_CONFIGURED
+ USBD_RESETING, // DEVICE_RESET
+ USBD_OK, // DEVICE_ADDRESS_ASSIGNED
+ USBD_OK, // DEVICE_CONFIGURED
+ USBD_OK, // DEVICE_SET_INTERFACE
+ USBD_OK, // DEVICE_SET_FEATURE
+ USBD_OK, // DEVICE_CLEAR_FEATURE
+ USBD_OK, // DEVICE_DE_CONFIGURED
+ USBD_SUSPENDED, // DEVICE_BUS_INACTIVE
+ USBD_OK, // DEVICE_BUS_ACTIVITY
+ USBD_RESETING, // DEVICE_POWER_INTERRUPTION
+ USBD_RESETING, // DEVICE_HUB_RESET
+ USBD_CLOSING, // DEVICE_DESTROY
+ USBD_CLOSED, // DEVICE_CLOSE
+};
+
+int usbd_device_reset(struct usbd_bus_instance *bus);
+int usbd_device_suspended(struct usbd_bus_instance *bus);
+int usbd_device_resumed(struct usbd_bus_instance *bus);
+
+
+/*!
+ * @brief pcd_bus_event_handler_irq() - called to respond to various usb events
+ *
+ * This is called from an interrupt context.
+ *
+ * Used by a Bus driver to indicate an event.
+ * @param bus
+ * @param event
+ * @param data
+ */
+void pcd_bus_event_handler_irq (struct usbd_bus_instance *bus, usbd_device_event_t event, int data)
+{
+ usbd_device_state_t last_state;
+ struct pcd_instance *pcd = (struct pcd_instance *)(bus ? bus->privdata : NULL);
+ usbd_device_event_t real_event = event;
+
+ RETURN_UNLESS(bus);
+
+ event = (event == DEVICE_ADDRESS_ASSIGNED) && !data ? DEVICE_RESET : event;
+
+ last_state = bus->device_state;
+
+ if (TRACE_VERBOSE)
+ switch (event) {
+ case DEVICE_BUS_INACTIVE:
+ TRACE_MSG0(pcd->TAG, "BUS INACTIVE");
+ break;
+ default:
+ TRACE_MSG0(pcd->TAG, "default");
+ break;
+ case DEVICE_UNKNOWN:
+ TRACE_MSG0(pcd->TAG, "UNKNOWN");
+ break;
+ case DEVICE_BUS_ACTIVITY:
+ TRACE_MSG0(pcd->TAG, "BUS ACTIVITY");
+ break;
+ case DEVICE_CONFIGURED:
+ TRACE_MSG0(pcd->TAG, "CONFIGURED");
+ break;
+ case DEVICE_SET_INTERFACE:
+ TRACE_MSG0(pcd->TAG, "SET INTEERFACE");
+ break;
+ case DEVICE_SET_FEATURE:
+ case DEVICE_CLEAR_FEATURE:
+ TRACE_MSG0(pcd->TAG, "SET/CLEAR FEATURE");
+ break;
+ }
+ /* get the next bus device state by table lookup of event, we need to save the
+ * current state IFF we are suspending so that it can be restored on resume.
+ */
+ switch (event) {
+ case DEVICE_BUS_INACTIVE:
+ bus->suspended_state = bus->device_state;
+ /* FALL THROUGH */
+ default:
+ bus->device_state = event_states[event];
+ break;
+ case DEVICE_UNKNOWN:
+ break;
+ case DEVICE_BUS_ACTIVITY:
+ bus->device_state = bus->suspended_state;
+ break;
+
+ case DEVICE_SET_INTERFACE:
+ case DEVICE_SET_FEATURE:
+ case DEVICE_CLEAR_FEATURE:
+ break;
+ }
+
+ /* set the bus status by table lookup of event.
+ */
+ switch (event) {
+ case DEVICE_BUS_ACTIVITY:
+ case DEVICE_BUS_INACTIVE:
+ BREAK_IF(USBD_CLOSING != bus->status);
+ /* FALL THROUGH */
+ default:
+ bus->status = event_status[event];
+ //TRACE_MSG1(pcd->TAG, "bus->status: %x", bus->status);
+ //TRACE_MSG0(PCD, "DEFAULT");
+ break;
+ }
+ TRACE_MSG5(pcd->TAG, "DEVICE_STATE event: %d data: %d state: %d -> %d status: %d",
+ event, data, last_state, bus->device_state, bus->status);
+
+ /* we need to reset things on some transitions
+ */
+ switch (bus->device_state) {
+ default:
+ // if we lost configuration then get rid of alternate settings
+ #if 0
+ if (bus->alternates) {
+ LKFREE(bus->alternates);
+ bus->alternates = NULL;
+ }
+ bus->bNumInterfaces = 0;
+ bus->device_feature_settings = 0;
+ bus->ConfigurationValue = 0;
+ #endif
+ case STATE_CONFIGURED:
+ break;
+ case STATE_SUSPENDED:
+ // XXX Checkme - is suspend equivalent to end of session?
+ // C.f. OTG 6.5.1-6.5.3 imply that these must be reset at the end of a session or reset
+ //bus->device_feature_settings &=
+ // ~(FEATURE(USB_OTG_A_HNP_ENABLE) | FEATURE(USB_OTG_B_HNP_ENABLE) | FEATURE(USB_OTG_A_ALT_HNP_ENABLE));
+ break;
+ }
+ /* Pass the event to the bus interface driver and then the function driver and
+ * any interface function drivers.
+ */
+ bus->driver->bops->event_handler (bus, real_event, data);
+
+ RETURN_UNLESS (bus->function_instance);
+
+ // XXX if (bus->function_instance->function_driver->fops->event_handler)
+ // XXX bus->function_instance->function_driver->fops->event_handler(bus->function_instance, event, data);
+
+ switch (event) {
+ case DEVICE_BUS_ACTIVITY:
+ TRACE_MSG0(pcd->TAG, "schedule RESUME_BH");
+ otg_workitem_start(bus->resume_bh);
+ break;
+ case DEVICE_BUS_INACTIVE:
+ TRACE_MSG0(pcd->TAG, "schedule SUSPEND_BH");
+ otg_workitem_start(bus->suspend_bh);
+ break;
+ case DEVICE_RESET:
+ TRACE_MSG0(pcd->TAG, "schedule RESET_BH");
+ otg_workitem_start(bus->reset_bh);
+ break;
+ default:
+ break;
+ }
+}
+/* ******************************************************************************************* */
+/* Prevent overlapp of bi administrative functions mainly:
+ * pcd_bus_register
+ * pcd_bus_deregister
+ */
+//DECLARE_MUTEX (usbd_bi_sem);
+struct usbd_bus_instance *usbd_bus;
+
+
+/*!
+ * @brief pcd_device_bh() - Bottom half handler to process sent or received urbs.
+ * @param data - pcd_instance pointer
+ */
+void *pcd_device_bh (otg_task_arg_t data)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *) data;
+ struct usbd_bus_instance *bus = pcd->bus;
+ struct usbd_endpoint_instance *endpoint;
+ struct usbd_urb *urb;
+ int i;
+ RETURN_NULL_IF (!bus || !(endpoint = bus->endpoint_array));
+ //otg_led(LED2, TRUE);
+ for (i = 0; i < bus->endpoints; i++) {
+ struct usbd_endpoint_instance *endpoint = &bus->endpoint_array[i];
+ CONTINUE_UNLESS(endpoint);
+ for ( ; (urb = usbd_first_finished_urb_detached (endpoint, &endpoint->rdy)); ) {
+ #ifdef CONFIG_OTG_LATENCY_CHECK
+ otg_tick_t ticks;
+ u16 framenum;
+ otg_tick_t urb_ticks = urb->ticks;
+
+ /* elapsed time to get BH started and find this urb */
+ TRACE_ELAPSED(pcd->TAG, "PCD 1. BH", urb->ticks, (u32)urb);
+ otg_get_ocd_info(pcd->otg, &ticks, &framenum);
+ #endif
+ usbd_do_urb_callback (urb, urb->status);
+
+ #ifdef CONFIG_OTG_LATENCY_CHECK
+ /* elapsed time for the callback */
+ TRACE_ELAPSED(pcd->TAG, "PCD 2. CB", ticks, (u32)urb);
+
+ /* elapsed time for all processing since the urb was finished */
+ TRACE_ELAPSED(pcd->TAG, "PCD 3. TOTAL", urb_ticks, (u32)urb);
+ #endif
+ }
+ }
+ //otg_led(LED2, FALSE);
+ return NULL;
+}
+
+/*! pcd_startup_events - called for start event processing
+ * @param bus - bus instance pointer
+ */
+void pcd_startup_events(struct usbd_bus_instance *bus)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ TRACE_MSG0(pcd->TAG, "BI_STARTUP");
+ if (usbd_pcd_ops.startup_events) {
+ TRACE_MSG0(pcd->TAG, "BI_STARTUP_EVENTS - udc");
+ usbd_pcd_ops.startup_events(pcd);
+ }
+ else {
+ TRACE_MSG0(pcd->TAG, "BI_STARTUP_EVENTS - default");
+ pcd_bus_event_handler_irq (bus, DEVICE_INIT, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_CREATE, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_HUB_CONFIGURED, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_RESET, 0);
+ }
+}
+
+/*! pcd_register_bh -
+ * @param data - pcd_instance pointer
+ */
+void *pcd_register_bh(void *data)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *) data;
+ struct otg_instance *otg = pcd->otg;
+ struct usbd_bus_instance *bus = NULL;
+ struct usbd_endpoint_instance *endpoint;
+ BOOL usbd_enabled = FALSE;
+
+
+ TRACE_MSG1(pcd->TAG, "BUS_REGISTER_BH: pcd: %p", (int)data);
+ RETURN_NULL_UNLESS(pcd);
+
+ TRACE_MSG0(pcd->TAG, "BUS_REGISTER_BH");
+
+ bus_driver.name = usbd_pcd_ops.name;
+ bus_driver.max_endpoints = usbd_pcd_ops.max_endpoints;
+ bus_driver.maxpacketsize = usbd_pcd_ops.ep0_packetsize;
+ bus_driver.high_speed_capable = usbd_pcd_ops.high_speed_capable;
+ //bus_driver.capabilities = usbd_pcd_ops.capabilities;
+ bus_driver.bMaxPower = usbd_pcd_ops.bMaxPower;
+ bus_driver.ports = usbd_pcd_ops.ports;
+ // XXX bus_driver.otg_bmAttributes = tcd_ops.bmAttributes;
+ bus_driver.bmAttributes = usbd_pcd_ops.bmAttributes;
+
+
+ TRACE_MSG1(pcd->TAG, "usbd_pcd_ops.bmAttributes: %x", usbd_pcd_ops.bmAttributes);
+
+ /* register this bus interface driver and create the device driver instance */
+ THROW_UNLESS ( (bus = usbd_register_bus (&bus_driver, usbd_pcd_ops.ep0_packetsize)), error);
+ bus->otg = otg;
+
+ bus->privdata = (void *) pcd;
+ bus->high_speed = USB_SPEED_FULL;
+ pcd->bus = bus;
+
+ #if 0
+ if (serial_number_str && strlen(serial_number_str)) {
+ char *sp, *dp;
+ int i;
+ TRACE_MSG1(pcd->TAG, "prm serial_number_str: %s", serial_number_str);
+ for (sp = serial_number_str, dp = otg->serial_number, i = 0;
+ *sp && (i < (sizeof(otg->serial_number) - 1)); i++, sp++)
+ if (isxdigit(*sp)) *dp++ = toupper(*sp);
+ }
+ #endif
+
+ TRACE_STRING(pcd->TAG, "otg serial_number_str: %s", pcd->otg->serial_number);
+
+ if (pcd->otg && pcd->otg->function_name)
+ TRACE_STRING(pcd->TAG, "function_name: %s", pcd->otg->function_name);
+
+
+ THROW_IF ((usbd_enabled = usbd_enable_function (bus, pcd->otg->function_name, pcd->otg->serial_number)), error);
+
+ TRACE_MSG0(pcd->TAG, "BUS_REGISTER_BH: task start");
+ THROW_UNLESS((pcd->task = otg_task_init2("usbd", pcd_device_bh, pcd, pcd->TAG)), error);
+ //pcd->task->debug = TRUE;
+ otg_task_start(pcd->task);
+
+ /* setup endpoint zero */
+ endpoint = bus->endpoint_array + 0;
+ endpoint->bEndpointAddress[0] = endpoint->bEndpointAddress[1] = 0;
+ endpoint->wMaxPacketSize[0] = endpoint->wMaxPacketSize[1] = usbd_pcd_ops.ep0_packetsize;
+ endpoint->rcv_transferSize = 4096; // XXX should this be higher
+
+ if (usbd_pcd_ops.setup_ep) usbd_pcd_ops.setup_ep (pcd, 0, endpoint);
+
+ TRACE_MSG0(pcd->TAG, "BUS_REGISTER_BH: startup");
+ pcd_startup_events (bus);
+
+ TRACE_MSG0(pcd->TAG, "BUS_REGISTER_BH: PCD_OK");
+ otg_event(pcd->otg, PCD_OK, pcd->TAG, "PCD_REGISTER_BH PCD_OK");
+
+ TRACE_MSG0(pcd->TAG, "BUS_REGISTER_BH: FINISHED");
+
+ CATCH(error) {
+
+ TRACE_MSG0(pcd->TAG, "PCD_REGISTER_BH: register failed");
+ otg_event(pcd->otg, enable_otg_ | PCD_OK, pcd->TAG, "BUS_REGISTER_BH");
+
+ if (pcd->task) {
+ otg_task_exit(pcd->task);
+ pcd->task = NULL;
+ }
+
+ if (usbd_enabled) {
+ usbd_disable_function (bus);
+ }
+
+ if (bus) {
+ usbd_deregister_bus (bus);
+ pcd->bus = NULL;
+ }
+ }
+ return NULL;
+}
+
+/*! pcd_deregister_bh -
+ * @param data - pcd_instance pointer
+ */
+void *pcd_deregister_bh(void *data)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *) data;
+ struct usbd_bus_instance *bus;
+ //struct bus_data *data;
+
+ TRACE_MSG2(pcd->TAG, "PCD_DEREGISTER_BH: pcd: %x bus: %x", pcd, pcd ? pcd->bus : NULL);
+
+ //Disable OTG state
+ otg_event(pcd->otg, enable_otg_, pcd->TAG, "BUS_DEREGISTER_BH");
+
+ if (pcd && (bus = pcd->bus) /*&& (usbd_bus_state_enabled == bus->bus_state)*/) {
+
+ //otg_up_admin(pcd->task);
+ //otg_task_exit(bus->task);
+ //bus->task = NULL;
+
+ //TRACE_MSG0(pcd->TAG, "BUS_DEREGISTER_BH: task stop");
+ //otg_task_exit(pcd->task);
+ //pcd->task = NULL;
+
+ if (usbd_pcd_ops.disable) usbd_pcd_ops.disable (pcd);
+
+ if (bus->device_state == STATE_ATTACHED) {
+ pcd_bus_event_handler_irq (bus, DEVICE_RESET, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_POWER_INTERRUPTION, 0);
+ pcd_bus_event_handler_irq (bus, DEVICE_HUB_RESET, 0);
+ }
+
+ pcd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0);
+ pcd_disable_endpoints (bus);
+ pcd_disable (bus);
+
+ TRACE_MSG0(pcd->TAG, "BUS_DEREGISTER_BH: task stop");
+ if (pcd->task) otg_task_exit(pcd->task);
+ pcd->task = NULL;
+
+ usbd_disable_function (bus);
+
+ //if (bus->serial_number_str)
+ // lkfree (pcd->bus->serial_number_str);
+
+ usbd_deregister_bus (bus);
+ pcd->bus = NULL;
+
+ }
+ TRACE_MSG0(pcd->TAG, "sending PCD_OK");
+ otg_event(pcd->otg, PCD_OK, pcd->TAG, "PCD_DEREGISTER_BH PCD_OK");
+ return NULL;
+}
+
+/* ************************************************************************************* */
+
+
+/*! pcd_framenum() - get current framenum
+ * @param otg
+ */
+u16
+pcd_framenum (struct otg_instance *otg)
+{
+ struct pcd_instance *pcd = otg ? otg->pcd : NULL;
+ RETURN_ZERO_UNLESS(otg && pcd && usbd_pcd_ops.framenum);
+ return usbd_pcd_ops.framenum(pcd);
+}
+
+
+/*!
+ * pcd_remote_wakeup() - perform remote wakeup.
+ * Initiate a remote wakeup to the host.
+ * @param otg - otg instance
+ * @param flag - SET or RESET
+ */
+void pcd_remote_wakeup(struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ struct usbd_bus_instance *bus = pcd->bus;
+
+ struct otg_dev *otg_dev = otg->privdata;
+
+ TRACE_MSG1(pcd->TAG, "device_feature_settings: %04x", bus->device_feature_settings);
+ RETURN_UNLESS(bus->device_feature_settings & FEATURE(USB_DEVICE_REMOTE_WAKEUP));
+
+ TRACE_MSG1(pcd->TAG, "remote wakeup enabled flag: %d", flag);
+
+ RETURN_UNLESS (usbd_pcd_ops.remote_wakeup);
+
+ usbd_pcd_ops.remote_wakeup(pcd);
+}
+
+/*!
+ * pcd_tcd_en_func() - enable
+ * This is called to enable / disable the PCD and USBD stack.
+ * @param otg - otg instance
+ * @param flag - SET or RESET
+ *
+ * Start or stop the UDC.
+ *
+ * SET Called after Vbus detected and before the pullup is enabled.
+ *
+ * RESET after pullup disabled to turn off.
+ *
+ */
+void
+pcd_tcd_en_func (struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+
+ int vbus = FALSE;
+
+ RETURN_UNLESS (usbd_pcd_ops.vbus_status);
+
+ vbus = usbd_pcd_ops.vbus_status(pcd);
+
+ TRACE_MSG1(pcd->TAG, "vbus: %d", vbus);
+
+ switch (flag) {
+ case PULSE:
+ case SET:
+ switch (vbus) {
+ case 1:
+ case 2:
+ otg_event(otg, VBUS_VLD | B_SESS_VLD, otg->pcd->TAG, "PCD TCD EN");
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+/*! pcd_dp_pullup_func
+ * @param otg - otg_instance pointer
+ * @param flag -
+ *
+ * Enable or disable pullup.
+ */
+void pcd_dp_pullup_func(struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+
+ RETURN_UNLESS(usbd_pcd_ops.softcon);
+ usbd_pcd_ops.softcon(pcd, flag);
+}
+
+
+/*! pcd_en_func - enable
+ *
+ * This is called to enable / disable the PCD and USBD stack.
+ * @param otg - otg instance pointer
+ * @param flag - enable/disable flag
+ */
+void pcd_en_func (struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ struct usbd_bus_instance *bus = pcd->bus;
+
+ //TRACE_MSG0(pcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(pcd->TAG, "PCD_EN: SET");
+ // check if we can see the UDC and register, then enable the function
+ if (usbd_pcd_ops.enable) usbd_pcd_ops.enable (pcd);
+ break;
+
+ case RESET:
+ TRACE_MSG0(pcd->TAG, "PCD_EN: RESET");
+ pcd_bus_event_handler_irq (bus, DEVICE_RESET, 0);
+ //TRACE_MSG0(pcd->TAG, "PCD_EN: DESTROY");
+ //pcd_bus_event_handler_irq (bus, DEVICE_DESTROY, 0);
+ TRACE_MSG0(pcd->TAG, "PCD_EN: FINISHED");
+ // XXX need to set a flag here
+ break;
+ }
+}
+
+/*! pcd_init_func - per peripheral controller common initialization
+ *
+ * This is called to initialize / de-initialize the PCD and USBD stack.
+ *
+ * We start work items to do this.
+ * @param otg - otg instance pointer
+ * @param flag - operation flag
+ *
+ */
+void pcd_init_func (struct otg_instance *otg, u8 flag)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ //struct usbd_bus_instance *bus = pcd->bus;
+ //struct bus_data *data = NULL;
+
+ //TRACE_MSG0(pcd->TAG, "--");
+ switch (flag) {
+ case SET:
+ TRACE_MSG0(pcd->TAG, "PCD_INIT: SET - start REGISTER BH");
+ //printk(KERN_INFO"%s: SET pcd->register_bh: %p\n", __FUNCTION__, pcd->register_bh);
+ otg_workitem_start(pcd->register_bh);
+ //TRACE_MSG0(pcd->TAG, "BUS_REGISTER_SCHEDULE: finished");
+ break;
+
+ case RESET:
+ TRACE_MSG0(pcd->TAG, "PCD_INIT: RESET - start DEREGISTER BH");
+ otg_workitem_start(pcd->deregister_bh);
+ //TRACE_MSG0(pcd->TAG, "BUS_DEREGISTER_SCHEDULE: finished");
+ }
+}
+
+/*! pcd_mod_init - per peripheral controller common initialization
+ *
+ * This is called to initialize / de-initialize the PCD and USBD stack.
+ *
+ * We start work items to do this.
+ * @param otg - otg instance pointer
+ *
+ */
+int pcd_mod_init (struct otg_instance *otg)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ //struct usbd_bus_instance *bus = pcd->bus;
+
+
+ THROW_UNLESS((pcd->register_bh = otg_workitem_init("regbh", pcd_register_bh, pcd, pcd->TAG)), error);
+ THROW_UNLESS((pcd->deregister_bh = otg_workitem_init("deregbh", pcd_deregister_bh, pcd, pcd->TAG)), error);
+ //pcd->register_bh->debug = TRUE;
+ //pcd->deregister_bh->debug = TRUE;
+ return 0;
+
+ CATCH(error) {
+ if (pcd->register_bh) otg_workitem_exit(pcd->register_bh);
+ if (pcd->deregister_bh) otg_workitem_exit(pcd->deregister_bh);
+ pcd->register_bh = pcd->deregister_bh = NULL;
+ return -EINVAL;
+ }
+}
+
+/*! pcd_mod_exit - per peripheral controller common initialization
+ *
+ * This is called to initialize / de-initialize the PCD and USBD stack.
+ *
+ * We start work items to do this.
+ * @param otg - otg_instance pointer
+ *
+ */
+void pcd_mod_exit (struct otg_instance *otg)
+{
+ struct pcd_instance *pcd = otg->pcd;
+ //struct usbd_bus_instance *bus = pcd->bus;
+ //
+
+ if (pcd->task) otg_task_exit(pcd->task);
+ if (pcd->register_bh) otg_workitem_exit(pcd->register_bh);
+ if (pcd->deregister_bh) otg_workitem_exit(pcd->deregister_bh);
+
+ pcd->task = NULL;
+ pcd->register_bh = pcd->deregister_bh = NULL;
+}
+
+struct pcd_ops pcd_ops = {
+ .pcd_en_func = pcd_en_func,
+ .pcd_init_func = pcd_init_func,
+ #ifdef CONFIG_OTG_REMOTE_WAKEUP
+ .remote_wakeup_func = pcd_remote_wakeup,
+ #endif /* CONFIG_OTG_REMOTE_WAKEUP */
+ .framenum = pcd_framenum,
+ .tcd_en_func = pcd_tcd_en_func,
+ .dp_pullup_func = pcd_dp_pullup_func,
+ .ticks = pcd_ticks,
+ .elapsed = pcd_elapsed,
+};
diff --git a/drivers/otg/hardware/zasevb-arc-l26.c b/drivers/otg/hardware/zasevb-arc-l26.c
new file mode 100644
index 000000000000..ff48df99d319
--- /dev/null
+++ b/drivers/otg/hardware/zasevb-arc-l26.c
@@ -0,0 +1,192 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/zasevb-arc-l26.c - iMX31ADS EVB OTG Peripheral and OTG Controller Drivers Module Initialization
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/zasevb/zasevb-arc-l26.c|20070909224442|30419
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @addtogroup MX31 High Speed
+ * @ingroup Hardware
+ */
+/*!
+ * @file otg/hardware/zasevb-arc-l26.c
+ * @brief MX31 USB Host Controller Driver
+ *
+ * Linux MX31 OTG PCD/OCD/TCD Driver Initialization
+ *
+ * This file selects and initializes all of the low level hardware drivers
+ * for the MX31 High Speed.
+ *
+ * Notes.
+ *
+ *
+ * @ingroup MX31
+ * @ingroup MXC
+ * @ingroup LINUXOS
+ */
+/*
+ * XXX Need to change mx31_ function names to arc_
+ */
+
+#include <otg/pcd-include.h>
+#include <otg/otg-dev.h>
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <asm/arch/gpio.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+#include <asm/arch/board.h>
+#endif
+
+//#include "mxc-lnx.h"
+//#include "mxc-hardware.h"
+
+MOD_AUTHOR ("sl@belcarra.com");
+MOD_DESCRIPTION ("Belcarra MX31");
+EMBED_LICENSE();
+
+
+MOD_PARM_STR(serial_number_str, "Serial Number String", NULL);
+
+
+/* ************************************************************************************* */
+
+extern int arc_dev_module_init(struct otg_device_driver *otg_device_driver,
+ int (*device_probe)(struct device *),
+ int (*device_remove)(struct device *)
+ );
+
+extern void arc_dev_module_exit(struct otg_device_driver *);
+
+//extern int arc_ocd_module_init (struct otg_device_driver *);
+//extern void arc_ocd_module_exit (struct otg_device_driver *);
+
+
+extern int arc_pcd_module_init (struct otg_device_driver *);
+extern void arc_pcd_module_exit (struct otg_device_driver *);
+
+//extern int arc_tcd_module_init (struct otg_device_driver *);
+//extern void arc_tcd_module_exit (struct otg_device_driver *);
+
+
+static struct otg_device_driver arc_otg_device_driver = {
+ .name = "arc_udc",
+};
+
+
+
+/*! zasevb_arc_device_probe - called to initialize platform
+ * @param device - device
+ *
+ * This is used to call the dev level probe functions with
+ * the additional otg_device_driver structure.
+ */
+static int
+zasevb_arc_device_probe(struct device *device)
+{
+ printk(KERN_INFO"%s: l26\n", __FUNCTION__);
+ RETURN_ZERO_UNLESS (arc_otg_device_driver.probe);
+ return arc_otg_device_driver.probe(device, &arc_otg_device_driver);
+}
+
+/*! zasevb_arc_device_remove- called to remote device
+ * @param device - device
+ *
+ * This is used to call the dev level probe functions with
+ * the additional otg_device_driver structure.
+ */
+static int
+zasevb_arc_device_remove(struct device *device)
+{
+ int rc;
+ printk(KERN_INFO"%s: l26\n", __FUNCTION__);
+ RETURN_ZERO_UNLESS (arc_otg_device_driver.remove);
+ rc = arc_otg_device_driver.remove(device, &arc_otg_device_driver);
+ return rc;
+}
+
+
+/*!
+ * zasevb_arc_modexit() - This is used as module exit, and as cleanup if modinit fails.
+ */
+static void zasevb_arc_modexit (void)
+{
+ /* unload the dev driver, this will stop otg and destroy
+ * the otg_dev and otg instances etc.
+ */
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ arc_dev_module_exit(&arc_otg_device_driver);
+
+ /* cleanup the rest of the sub-drivers
+ */
+ arc_pcd_module_exit(&arc_otg_device_driver);
+ //arc_tcd_module_exit(&arc_otg_device_driver);
+ //arc_ocd_module_exit(&arc_otg_device_driver);
+}
+
+/*!
+ * zasevb_arc_modinit() - linux module initialization
+ *
+ * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd
+ * for some architectures.
+ *
+ */
+static int zasevb_arc_modinit (void)
+{
+ int ocd = -1, tcd = -1, pcd = -1, dev = -1;
+
+ /* initialize all of the sub-drivers except for dev
+ */
+ //ocd = arc_ocd_module_init(&arc_otg_device_driver);
+ //tcd = arc_tcd_module_init(&arc_otg_device_driver);
+ pcd = arc_pcd_module_init(&arc_otg_device_driver);
+
+ /* ensure everything is ok until now */
+ //THROW_IF(ocd || tcd || pcd, error);
+ THROW_IF(pcd, error);
+
+ /* serial number needs to be set prior to initializing the dev
+ * driver
+ */
+ arc_otg_device_driver.serial_number = MODPARM(serial_number_str);
+
+ /* initialize the dev driver, this will get all sub-drivers
+ * started via their probe functions, create otg and otg_dev
+ * instances and finally start otg state machine.
+ */
+ THROW_IF((dev = arc_dev_module_init(&arc_otg_device_driver, zasevb_arc_device_probe, zasevb_arc_device_remove)), error);
+
+ return 0;
+
+ CATCH(error) {
+
+ printk(KERN_INFO"%s: FAILED\n", __FUNCTION__);
+
+ UNLESS (dev) arc_dev_module_exit(&arc_otg_device_driver);
+ //UNLESS (tcd) arc_tcd_module_exit(&arc_otg_device_driver);
+ UNLESS (pcd) arc_pcd_module_exit(&arc_otg_device_driver);
+ //UNLESS (ocd) arc_ocd_module_exit(&arc_otg_device_driver);
+
+ return -EINVAL;
+ }
+}
+
+module_init (zasevb_arc_modinit);
+module_exit (zasevb_arc_modexit);
diff --git a/drivers/otg/hardware/zasevb-isp1301.c b/drivers/otg/hardware/zasevb-isp1301.c
new file mode 100644
index 000000000000..5584dc88411b
--- /dev/null
+++ b/drivers/otg/hardware/zasevb-isp1301.c
@@ -0,0 +1,351 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/zasevb-isp1301.c -- ZASEVB ISP1301 Transceiver Controller driver
+ * @(#) sp/root@belcarra.com/debian-black.(none)|otg/platform/zasevb/zasevb-isp1301.c|20070822230929|14023
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/hardware/zasevb-isp1301.c
+ * @brief ZAS EVB USB ISP1301 Transceiver Driver
+ *
+ * This is a wrapper for the ISP1301 driver to support it on the ZASEVB.
+ *
+ * Notes
+ *
+ * 1. Ensure that S2.1 and S2.2 are both in the ON position.
+ *
+ * 2. The ISP1301 uses the system I2C bus, this is done throught the i2c-l26.c library.
+ * This library must be opened with the correct adapter name.
+ *
+ * 3. The ISP1301 uses a GPIO for interrupts. The GPIO must be properly setup using
+ * iomux().
+ *
+ * 4. The ISP1301 and USBOTG HWMODE must both be setup to match. Currently it
+ * appears that only the SEO-SEO / DAT-Bidirectional combination works correctly.
+ *
+ * @ingroup ZASEVB
+ * @ingroup TCD
+ *
+ */
+
+
+#include <otg/pcd-include.h>
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301) || defined(_OTG_DOXYGEN)
+
+#include <asm/arch/gpio.h>
+
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+
+#include <linux/delay.h>
+
+#ifndef PLL2_BASE_ADDR
+#define PLL2_BASE_ADDR 0x5004C000
+#endif
+#define PLL2_DP_CTL IO_ADDRESS(PLL2_BASE_ADDR + 0x00)
+#define PLL2_DP_CONFIG IO_ADDRESS(PLL2_BASE_ADDR + 0x04)
+#define PLL2_DP_OP IO_ADDRESS(PLL2_BASE_ADDR + 0x08)
+#define PLL2_DP_MFD IO_ADDRESS(PLL2_BASE_ADDR + 0x0c)
+#define PLL2_DP_MFN IO_ADDRESS(PLL2_BASE_ADDR + 0x10)
+#define PLL2_DP_HFSOP IO_ADDRESS(PLL2_BASE_ADDR + 0x1c)
+#define PLL2_DP_HFSMFD IO_ADDRESS(PLL2_BASE_ADDR + 0x20)
+#define PLL2_DP_HFSMFN IO_ADDRESS(PLL2_BASE_ADDR + 0x24)
+
+
+
+#if defined(CONFIG_MACH_ARGONPLUSEVB) || defined(CONFIG_ARCH_ARGONPLUSEVB) || defined(CONFIG_ARCH_ARGONLV)
+#define ZGPIO_PORT 0
+#define ZGPIO_PIN 2
+#endif/* defined(CONFIG_MACH_ARGONPLUSEVB)*/
+
+#if defined(CONFIG_MACH_SCMA11EVB) || defined(CONFIG_ARCH_SCMA11EVB)
+#define ZGPIO_PORT 2
+#define ZGPIO_PIN 12
+#endif /* CONFIG_MACH_SCMA11EVB */
+
+#ifdef CONFIG_ARCH_ZEUS
+#define ZGPIO_PORT 1
+#define ZGPIO_PIN 30
+#endif /* CONFIG_ARCH_ZEUS */
+
+
+void mxc_set_transceiver_mode(int mode);
+void mxc_main_clock_on(void);
+void mxc_disable_interrupts (void);
+void isp1301_exit(void);
+int mxc_iomux_gpio_isp1301_set (struct otg_instance *otg, int);
+int mxc_iomux_gpio_isp1301_reset (struct otg_instance *otg);
+
+
+
+#if 0 //removing gpios
+
+/* ********************************************************************************************* */
+/*!
+ * zasevb_gpio_int_hndlr() - gpio interrupt handler
+ * @param irq
+ * @param dev_id
+ * @param regs
+ * @return interrupt handler status
+ * This disables the gpio interrup and schedules the isp1301 bottom half handler.
+ *
+ */
+static irqreturn_t zasevb_gpio_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ gpio_config_int_en(ZGPIO_PORT, ZGPIO_PIN, FALSE);
+
+ TRACE_MSG0(otg->tcd->TAG, "ZASEVB GPIO INTERRUPT: SCHEDULE WORK");
+ isp1301_bh_wakeup(REMOVE_tcd_instance->otg, FALSE);
+
+ return IRQ_HANDLED;
+}
+
+/*!
+ * zasevb_isp1301_bh()- call isp1301 bottom half handler
+ * @param arg
+ * This is a wrapper to the isp1301 bottom half handler, it
+ * re-enables the gpio interrupt after processing complete.
+ */
+void zasevb_isp1301_bh(void *arg)
+{
+ TRACE_MSG0(otg->tcd->TAG, "ZASEVB GPIO INTERRUPT: ISP1301_BH");
+ isp1301_bh(arg);
+ TRACE_MSG0(otg->tcd->TAG, "ZASEVB GPIO INTERRUPT: REENABLE");
+ gpio_config_int_en(ZGPIO_PORT, ZGPIO_PIN, TRUE);
+}
+
+#endif
+
+/* ********************************************************************************************* */
+extern int zasevb_tcd_mod_init (struct otg_instance *otg);
+extern void zasevb_tcd_mod_exit (struct otg_instance *otg);
+//struct tcd_instance *zasevb_tcd_instance;
+#if !defined(OTG_C99)
+struct tcd_ops tcd_ops;
+/*!
+ * zasevb_tcd_global_init() - non c99 global initializer
+ */
+void zasevb_tcd_global_init(void)
+{
+ ZERO(tcd_ops);
+
+ tcd_ops.vbus = isp1301_vbus;
+ tcd_ops.id = isp1301_id;
+
+ tcd_ops.tcd_init_func = isp1301_tcd_init;
+ tcd_ops.tcd_en_func = isp1301_tcd_en;
+ tcd_ops.chrg_vbus_func = isp1301_chrg_vbus;
+ tcd_ops.drv_vbus_func = isp1301_drv_vbus;
+ tcd_ops.dischrg_vbus_func = isp1301_dischrg_vbus;
+ tcd_ops.dp_pullup_func = isp1301_dp_pullup_func;
+ tcd_ops.dm_pullup_func = isp1301_dm_pullup_func;
+ tcd_ops.dp_pulldown_func = isp1301_dp_pulldown_func;
+ tcd_ops.dm_pulldown_func = isp1301_dm_pulldown_func;
+
+ tcd_ops.overcurrent_func = NULL;
+ tcd_ops.dm_det_func = isp1301_dm_det_func;
+ tcd_ops.dp_det_func = isp1301_dp_det_func;
+ tcd_ops.cr_det_func = isp1301_cr_det_func;
+ tcd_ops.peripheral_host_func = isp1301_peripheral_host_func;
+ //tcd_ops.mx21_vbus_drain_func = isp1301_mx21_vbus_drain_func;
+ tcd_ops.id_pulldown_func = isp1301_id_pulldown_func;
+ tcd_ops.audio_func = isp1301_audio_func;
+ tcd_ops.uart_func = isp1301_uart_func;
+ tcd_ops.mono_func = isp1301_mono_func;
+
+ tcd_ops.mod_init = zasevb_tcd_mod_init;
+ tcd_ops.mod_exit = zasevb_tcd_mod_exit;
+}
+#else /* !defined(OTG_C99) */
+struct tcd_ops tcd_ops = {
+
+ //.vbus = isp1301_vbus,
+ //.id = isp1301_id,
+
+ .tcd_init_func = isp1301_tcd_init,
+ .tcd_en_func = isp1301_tcd_en,
+ .chrg_vbus_func = isp1301_chrg_vbus,
+ .drv_vbus_func = isp1301_drv_vbus,
+ .dischrg_vbus_func = isp1301_dischrg_vbus,
+ .dp_pullup_func = isp1301_dp_pullup_func,
+ .dm_pullup_func = isp1301_dm_pullup_func,
+ .dp_pulldown_func = isp1301_dp_pulldown_func,
+ .dm_pulldown_func = isp1301_dm_pulldown_func,
+
+ .overcurrent_func = NULL,
+ .dm_det_func = isp1301_dm_det_func,
+ .dp_det_func = isp1301_dp_det_func,
+ .cr_det_func = isp1301_cr_det_func,
+ .peripheral_host_func = isp1301_peripheral_host_func,
+ //.mx21_vbus_drain_func = isp1301_mx21_vbus_drain_func,
+ .id_pulldown_func = isp1301_id_pulldown_func,
+ .audio_func = isp1301_audio_func,
+ .uart_func = isp1301_uart_func,
+ .mono_func = isp1301_mono_func,
+
+ .mod_init = zasevb_tcd_mod_init,
+ .mod_exit = zasevb_tcd_mod_exit,
+};
+#endif /* !defined(OTG_C99) */
+
+/* ********************************************************************************************* */
+void zasevb_tcd_mod_exit (struct otg_instance *otg);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#define ADAPTER_NAME "mxc_i2c"
+#else
+#define ADAPTER_NAME "MXC I2C Adapter"
+#endif
+
+/*!
+ * zasevb_tcd_mod_init() - initial tcd setup
+ * This performs the platform specific hardware setup for the MX2ADS.
+ * @param otg - otg_instance pointer
+ */
+int zasevb_tcd_mod_init (struct otg_instance *otg)
+{
+ int i2c = 1;
+ int gpio = 1;
+
+ /* ------------------------------------------------------------------------ */
+ #if defined(CONFIG_OTG_ZASEVB_DIFFERENTIAL_UNIDIRECTIONAL)
+ int hwmode = XCVR_D_SE0_NEW;
+ int newmode = XCVR_D_D;
+ isp1301_tx_mode_t tx_mode = vp_vm_unidirectional; // SCMA11 ok
+ printk (KERN_INFO"Current setting is DIFFERENTIAL UNIDIRECTIONAL\n");
+
+ /* ------------------------------------------------------------------------ */
+ #elif defined(CONFIG_OTG_ZASEVB_SINGLE_ENDED_UNIDIRECTIONAL)
+ int hwmode = XCVR_SE0_D_NEW;
+ int newmode = XCVR_SE0_D_NEW;
+ isp1301_tx_mode_t tx_mode = dat_se0_unidirectional; // ArgonEVB ok
+ printk (KERN_INFO"Current setting is SINGLE ENDED UNIDIRECTIONAL\n");
+
+ /* ------------------------------------------------------------------------ */
+ #elif defined(CONFIG_OTG_ZASEVB_DIFFERENTIAL_BIDIRECTIONAL)
+ int hwmode = XCVR_D_D;
+ int newmode = XCVR_D_D;
+ isp1301_tx_mode_t tx_mode = vp_vm_bidirectional; // ArgonEVB ok
+ printk (KERN_INFO"Current setting is DIFFERENTIAL BIDIRECTIONAL\n");
+
+ /* ------------------------------------------------------------------------ */
+ #elif defined(CONFIG_OTG_ZASEVB_SINGLE_ENDED_BIDIRECTIONAL)
+ int hwmode = XCVR_SE0_SE0;
+ int newmode = XCVR_SE0_SE0;
+ isp1301_tx_mode_t tx_mode = dat_se0_bidirectional; //SCMA11 ok
+ printk (KERN_INFO"Current setting is SINGLE ENDED BIDIRECTIONAL\n");
+
+ /* ------------------------------------------------------------------------ */
+ #else
+ #error Please Configure Transceiver Mode
+ #endif /* CONFIG_OTG_ZASEVB_.... */
+ /* ------------------------------------------------------------------------ */
+
+ TRACE_MSG0(otg->tcd->TAG, "1. I2C setup");
+
+ THROW_IF ((i2c = i2c_configure(ADAPTER_NAME, ISP1301_I2C_ADDR_HIGH)), error);
+
+ TRACE_MSG0(otg->tcd->TAG, "2. ISP1301 module setup");
+ // isp1301_mod_init(&zasevb_isp1301_bh);
+
+ //TRACE_MSG0(otg->tcd->TAG, "3. SET TCD OPS");
+ //printk(KERN_INFO"%s: set_tcd_ops\n", __FUNCTION__);
+ //THROW_UNLESS(zasevb_tcd_instance = otg_set_tcd_ops(otg, &tcd_ops), error);
+
+ TRACE_MSG0(otg->tcd->TAG, "4. ISP1301 device setup");
+
+ mxc_iomux_gpio_isp1301_set (otg, hwmode);
+
+ #ifdef CONFIG_ARCH_MXC91131
+ writel (0x00000051, PLL2_DP_HFSOP);
+ writel (0x00000051, PLL2_DP_OP);
+ #endif /* CONFIG_ARCH_ZEUS */
+
+
+ /* ------------------------------------------------------------------------ */
+ TRACE_MSG0(otg->tcd->TAG, "7. SET HWMODE");
+ isp1301_configure(otg, tx_mode, spd_susp_reg);
+ mxc_main_clock_on();
+ //mxc_host_clock_on();
+ //mxc_func_clock_on();
+ mxc_set_transceiver_mode(newmode);
+
+
+
+#if 0 //Test reading registers
+
+ int int_src1, int_lat;
+ for (;;){
+ int_src1 = i2c_readb(ISP1301_INTERRUPT_SOURCE);
+ int_lat = i2c_readb(ISP1301_INTERRUPT_LATCH_CLR);
+ printk(KERN_INFO"%s: interrupt source: %x latch: %x\n", __FUNCTION__, int_src1, int_lat);
+ mdelay (1000);
+ i2c_writeb(ISP1301_INTERRUPT_LATCH_CLR, int_lat);
+ mdelay (1000);
+ }
+
+#endif
+
+#if 0 //pulse on DP
+ for (;;){
+ mdelay (100);
+ i2c_writeb(ISP1301_OTG_CONTROL_SET, ISP1301_DP_PULLUP);
+ mdelay (100);
+ i2c_writeb(ISP1301_OTG_CONTROL_CLR, ISP1301_DP_PULLUP);
+ }
+#endif
+
+ /* Success!
+ */
+ TRACE_MSG0(otg->tcd->TAG, "8. Success!");
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ UNLESS (i2c) i2c_close();
+ // UNLESS (gpio) gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+ TRACE_MSG0(otg->tcd->TAG, "MX2_MOD_TCD_INIT FINISHED");
+ return 0;
+}
+
+/*!
+ * zasevb_tcd_mod_exit() - de-initialize
+ * This is called from mx2-ocd.c
+ * @param otg - otg_instance pointer
+ */
+void zasevb_tcd_mod_exit (struct otg_instance *otg)
+{
+ //struct otg_instance *otg = tcd_instance->otg;
+ TRACE_MSG0(otg->tcd->TAG, "ZASEVB_TCD_MOD_EXIT");
+ isp1301_mod_exit(otg);
+ mxc_disable_interrupts();
+ // gpio_free_irq (ZGPIO_PORT, ZGPIO_PIN, GPIO_HIGH_PRIO);
+ mxc_iomux_gpio_isp1301_reset(otg);
+ //zasevb_tcd_instance = otg_set_tcd_ops(otg, NULL);
+ isp1301_exit();
+ printk(KERN_INFO"%s: call i2c_close()\n", __FUNCTION__);
+ i2c_close();
+}
+#endif /* CONFIG_ZASEVB_ISP1301 */
diff --git a/drivers/otg/hardware/zasevb-l26.c b/drivers/otg/hardware/zasevb-l26.c
new file mode 100644
index 000000000000..70c5920c737a
--- /dev/null
+++ b/drivers/otg/hardware/zasevb-l26.c
@@ -0,0 +1,394 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/zasevb-l26.c - ZAS EVB OTG Peripheral and OTG Controller Drivers Module Initialization
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/zasevb/zasevb-l26.c|20070827195306|47636
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ */
+/*!
+ * @defgroup ZASEVB Freescale ZAS EVB
+ * @ingroup Hardware
+ */
+/*!
+ * @file otg/hardware/zasevb-l26.c
+ * @brief ZAS EVB USB Host Controller Driver
+ *
+ *
+ * Linux ZASEVB OTG PCD/HCD/OCD/TCD Driver Initialization
+ *
+ * This file initializes all of the low level hardware drivers for the ZAS EVB.
+ *
+ * Notes.
+ *
+ *
+ * @ingroup ZASEVB
+ * @ingroup LINUXOS
+ *
+ */
+
+#include <otg/pcd-include.h>
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <asm/arch/gpio.h>
+#include <linux/clk.h>
+
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+MOD_AUTHOR ("sl@belcarra.com");
+MOD_DESCRIPTION ("Belcarra MXC");
+EMBED_LICENSE();
+
+
+MOD_PARM_STR(serial_number_str, "Serial Number String", NULL);
+
+/* OTG Driver
+ */
+otg_tag_t REMOVE_OCD;
+struct ocd_instance *ocd_instance;
+extern struct ocd_ops ocd_ops;
+
+/* Transceiver Driver
+ */
+otg_tag_t REMOVE_TCD;
+struct tcd_instance *REMOVE_tcd_instance;
+extern struct tcd_ops tcd_ops;
+
+
+/* Peripheral Driver
+ */
+otg_tag_t REMOVE_PCD;
+struct pcd_instance *REMOVE_pcd_instance;
+#if !defined(CONFIG_USB_HOST)
+extern struct pcd_ops pcd_ops;
+#else /* !defined(CONFIG_USB_HOST) */
+irqreturn_t mxc_pcd_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
+{
+ return IRQ_HANDLED;
+}
+#endif /* !defined(CONFIG_USB_HOST) */
+
+/* Host Driver
+ */
+otg_tag_t HCD;
+struct hcd_instance *hcd_instance;
+#if defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE)
+extern struct hcd_ops hcd_ops;
+
+#else /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+irqreturn_t hcd_hw_int_hndlr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ return IRQ_HANDLED;
+}
+#endif /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+
+/* ************************************************************************************* */
+
+#if defined(CONFIG_OTG_ZASEVB_ISP1301) || defined(CONFIG_OTG_ZASEVB_ISP1301_MODULE)
+#define OTG_USE_I2C
+#else
+#define OTG_DISABLE_I2C
+#endif
+#ifdef OTG_USE_I2C
+extern int i2c_mod_init(struct otg_instance *otg);
+extern void i2c_mod_exit(struct otg_instance *otg);
+#endif
+extern int mxc_procfs_init(void);
+extern void mxc_procfs_exit(void);
+
+//extern void fs_mod_exit(void);
+
+#if !defined(OTG_C99)
+extern void pcd_global_init(void);
+extern void fs_ocd_global_init(void);
+extern void zasevb_tcd_global_init(void);
+extern void fs_pcd_global_init(void);
+#endif /* !defined(OTG_C99) */
+void mxc_pcd_ops_init(void);
+
+#if defined(CONFIG_OTG_GPTR)
+extern int mxc_gptcr_mod_init (int divisor, int multiplier);
+void mxc_gptcr_mod_exit (void);
+#endif /* defined(CONFIG_OTG_GPTR) */
+
+#if defined(CONFIG_OTG_HRT)
+extern int mxc_hrt_mod_init (struct otg_instance *otg, int divisor, int multiplier);
+void mxc_hrt_mod_exit (void);
+#endif /* defined(CONFIG_OTG_GPTR) */
+
+
+otg_tag_t ZAS;
+
+
+/*!
+ * zasevb_modexit() - This is used as module exit, and as cleanup if modinit fails.
+ */
+static void zasevb_modexit (void)
+{
+ struct otg_instance *otg = ocd_instance->otg;
+ //struct pcd_instance *pcd = (struct pcd_instance *)otg->pcd;
+ //struct usbd_bus_instance *bus= pcd->bus;
+
+ TRACE_MSG0(ZAS, "Modules exit!");
+
+ if (otg) otg_exit(otg);
+
+ mxc_procfs_exit();
+
+ /* Disable GPT
+ */
+ #if defined(CONFIG_OTG_GPTR)
+ mxc_gptcr_mod_exit();
+ #endif /* defined(CONFIG_OTG_GPTR) */
+
+ #if defined(CONFIG_OTG_HRT)
+ mxc_hrt_mod_exit();
+ #endif /* defined(CONFIG_OTG_GPTR) */
+
+ #ifdef OTG_USE_I2C
+ TRACE_MSG0(ZAS, "0. I2C");
+ i2c_mod_exit(otg);
+ #endif
+
+ #if !defined(CONFIG_USB_HOST)
+ if (pcd_ops.mod_exit) pcd_ops.mod_exit(otg);
+ REMOVE_pcd_instance = otg_set_pcd_ops(otg, NULL);
+ #else /* !defined(CONFIG_USB_HOST) */
+ printk(KERN_INFO"%s: PCD DRIVER N/A\n", __FUNCTION__);
+ #endif /* !defined(CONFIG_USB_HOST) */
+
+
+ #if defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE)
+ if (hcd_ops.mod_exit) hcd_ops.mod_exit(otg);
+ hcd_instance = otg_set_hcd_ops(otg, NULL);
+ //HCD = otg_trace_invalidate_tag(HCD);
+ #else /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+ printk(KERN_INFO"%s: HCD DRIVER N/A\n", __FUNCTION__);
+ #endif /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+
+
+ if (tcd_ops.mod_exit) tcd_ops.mod_exit(otg);
+ printk(KERN_INFO"%s: set_tcd_ops\n", __FUNCTION__);
+ REMOVE_tcd_instance = otg_set_tcd_ops(otg, NULL);
+ //REMOVE_TCD = otg_trace_invalidate_tag(REMOVE_TCD);
+
+ if (ocd_ops.mod_exit) ocd_ops.mod_exit(otg);
+ ocd_instance = otg_set_ocd_ops(otg, NULL);
+
+
+ ZAS = otg_trace_invalidate_tag(ZAS);
+
+
+ otg_destroy(otg);
+}
+
+extern void mxc_pcd_ops_init(void);
+
+/*!
+ * zasevb_modinit() - linux module initialization
+ *
+ * This needs to initialize the hcd, pcd and tcd drivers. This includes tcd and possibly hcd
+ * for some architectures.
+ *
+ */
+static int zasevb_modinit (void)
+{
+ struct otg_instance *otg = NULL;
+
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+ struct clk *clk = clk_get(NULL, "usb_clk");
+ clk_enable(clk);
+ clk_put(clk);
+ #endif
+
+ THROW_UNLESS((otg = otg_create()), error);
+
+ mxc_pcd_ops_init();
+ #if !defined(OTG_C99)
+ pcd_global_init();
+ fs_ocd_global_init();
+ zasevb_tcd_global_init();
+ fs_pcd_global_init();
+ #endif /* !defined(OTG_C99) */
+
+ ZAS = otg_trace_obtain_tag(otg, "zas");
+
+ mxc_procfs_init();
+
+ TRACE_MSG0(ZAS, "1. ZAS");
+
+ #if 0
+ /* ZAS EVB Platform setup
+ */
+ TRACE_MSG4(ZAS, "BCTRL Version: %04x Status: %04x 1: %04x 2: %04x",
+ readw(PBC_BASE_ADDRESS ),
+ readw(PBC_BASE_ADDRESS + PBC_BSTAT),
+ readw(PBC_BASE_ADDRESS + PBC_BCTRL1_SET),
+ readw(PBC_BASE_ADDRESS + PBC_BCTRL2_SET));
+
+ #endif
+
+ /* ZAS EVB Clock setup
+ */
+
+#if defined(CONFIG_ARCH_ARGONPLUS) || defined(CONFIG_ARCH_ARGONLV)
+#define ZASEVB_MULTIPLIER 12
+#define ZASEVB_DIVISOR 775 // ~10.
+#else
+#define ZASEVB_MULTIPLIER 12
+#define ZASEVB_DIVISOR 155
+#endif
+
+ TRACE_MSG0(ZAS, "2. Setup GPT");
+
+ THROW_UNLESS(ocd_instance = otg_set_ocd_ops(otg, &ocd_ops), error);
+ REMOVE_OCD = ocd_instance->TAG;
+ // XXX THROW_IF((ocd_ops.mod_init ? ocd_ops.mod_init() : 0), error);
+
+ #if defined(CONFIG_OTG_GPTR)
+ mxc_gptcr_mod_init(ZASEVB_DIVISOR, ZASEVB_MULTIPLIER);
+ #endif /* defined(CONFIG_OTG_GPTR) */
+
+ #if defined(CONFIG_OTG_HRT)
+ mxc_hrt_mod_init(otg, ZASEVB_DIVISOR, ZASEVB_MULTIPLIER);
+ #endif /* defined(CONFIG_OTG_GPTR) */
+
+
+ #if !defined(CONFIG_USB_HOST)
+ TRACE_MSG0(ZAS, "3. PCD");
+ THROW_UNLESS(REMOVE_pcd_instance = otg_set_pcd_ops(otg, &pcd_ops), error);
+ REMOVE_PCD = REMOVE_pcd_instance->TAG;
+ // XXX THROW_IF((pcd_ops.mod_init ? pcd_ops.mod_init() : 0), error);
+ #else /* !defined(CONFIG_USB_HOST) */
+ printk(KERN_INFO"%s: PCD DRIVER N/A\n", __FUNCTION__);
+ #endif /* !defined(CONFIG_USB_HOST) */
+
+
+ TRACE_MSG0(ZAS, "4. TCD");
+ THROW_UNLESS(REMOVE_tcd_instance = otg_set_tcd_ops(otg, &tcd_ops), error);
+ REMOVE_TCD = REMOVE_tcd_instance->TAG;
+ // XXX THROW_IF((tcd_ops.mod_init ? tcd_ops.mod_init() : 0), error);
+#ifdef OTG_USE_I2C
+ TRACE_MSG0(ZAS, "0. I2C");
+ i2c_mod_init(otg);
+#endif
+
+
+ #if defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE)
+ TRACE_MSG0(ZAS, "5. Host");
+ THROW_UNLESS(hcd_instance = otg_set_hcd_ops(otg, &hcd_ops), error);
+ HCD = hcd_instance->TAG;
+ // XXX THROW_IF((hcd_ops.mod_init) ? hcd_ops.mod_init() : 0, error);
+ #else /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+ printk(KERN_INFO"%s: HCD DRIVER N/A\n", __FUNCTION__);
+ #endif /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+
+
+
+ TRACE_MSG0(ZAS, "6. Init & check");
+ THROW_IF((ocd_ops.mod_init ? ocd_ops.mod_init(otg) : 0), error);
+
+ #if !defined(CONFIG_USB_HOST)
+ THROW_IF((pcd_ops.mod_init ? pcd_ops.mod_init(otg) : 0), error);
+ #endif /* !defined(CONFIG_USB_HOST) */
+
+ THROW_IF((tcd_ops.mod_init ? tcd_ops.mod_init(otg) : 0), error);
+
+ #if defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE)
+ THROW_IF((hcd_ops.mod_init) ? hcd_ops.mod_init(otg) : 0, error);
+ #endif /* defined(CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)|| defined(CONFIG_OTG_DEVICE) */
+
+ THROW_UNLESS(ocd_instance && (otg = ocd_instance->otg), error);
+
+
+ TRACE_MSG0(ZAS, "7. otg_init");
+ if (MODPARM(serial_number_str) && strlen(MODPARM(serial_number_str))) {
+
+ TRACE_MSG1(ZAS, "serial_number_str: %s", MODPARM(serial_number_str));
+ otg_serial_number (otg, MODPARM(serial_number_str));
+ }
+ otg_init(otg);
+
+ return 0;
+
+ CATCH(error) {
+ //zasevb_modexit();
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if 0
+/* ************************************************************************************* */
+static int __init_or_module
+zasevb_remove(struct device *dev)
+{
+ return 0;
+}
+
+
+static int __init
+zasevb_probe(struct device *dev)
+{
+}
+static int
+zasevb_suspend(struct device *dev, u32 state, u32 phase)
+{
+ return 0;
+}
+
+static int
+zasevb_resume(struct device *dev, u32 phase)
+{
+ return 0;
+}
+
+static struct device_driver zasevb_driver = {
+
+ .name = (char *) "ZASEVB-MXC-USBOTG,
+ .bus = &otg_bus_type,
+
+ .probe = zasevb_probe,
+ .remove = zasevb_remove,
+
+ .suspend = zasevb_suspend,
+ .resume = zasevb_resume,
+
+};
+
+
+static int __init zasevb_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+ return driver_register(&zasevb_driver);
+}
+
+static void __exit zasevb_exit(void)
+{
+ driver_unregister(&zasevb_driver);
+}
+#endif
+
+module_init (zasevb_modinit);
+module_exit (zasevb_modexit);
diff --git a/drivers/otg/hardware/zasevb-mc13783.c b/drivers/otg/hardware/zasevb-mc13783.c
new file mode 100644
index 000000000000..68d81df715c3
--- /dev/null
+++ b/drivers/otg/hardware/zasevb-mc13783.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
+ */
+/*
+ * otg/hardware/zasevb-mc13783.c -- Freescale mc13783 Connectivity driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/platform/zasevb/zasevb-mc13783.c|20070612232808|30186
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/hardware/zasevb-mc13783.c
+ * @brief mc13783 USB Transceiver Driver
+ *
+ * This is a wrapper for the Freescale MC13783 driver to support it on the ZASEVB.
+ *
+ * Notes
+ *
+ * 1. Ensure that S2.1 is ON and S2.2 is OFF.
+ *
+ * 2. Note that this driver has not been tested on the ZAS EVB.
+ *
+ * 3. The MC13783 Transceiver and USBOTG HWMODE must both be setup to match. Currently it
+ * appears that only the SEO-SEO / DAT-Bidirectional combination works correctly.
+ *
+ * @ingroup ZASEVB
+ * @ingroup TCD
+ *
+ */
+
+#include <asm/delay.h>
+
+#include <otg/pcd-include.h>
+#if defined(CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY) || defined(_OTG_DOXYGEN)
+
+#include <asm/arch/gpio.h>
+
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+
+/*
+ * These files are currently located in
+ * drivers/mxc/mc13783_legacy
+ */
+#include <core/mc13783_external.h>
+#include <core/mc13783_event.h>
+#include <module/mc13783_connectivity.h>
+
+/* ********************************************************************************************* */
+extern int mc13783_tcd_mod_init(struct otg_instance *otg);
+extern void mc13783_tcd_mod_exit(struct otg_instance *otg);
+struct tcd_instance *mc13783_tcd_instance;
+/* ********************************************************************************************* */
+int mxc_mc13783_vbus(struct otg_instance *otg);
+int mxc_mc13783_id(struct otg_instance *otg);
+void mxc_mc13783_tcd_en(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_tcd_init(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_chrg_vbus(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_drv_vbus(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dischrg_vbus(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dp_pullup_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dm_pullup_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dp_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dm_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_peripheral_host_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dm_det_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_dp_det_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_cr_det_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_bdis_acon_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_id_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_audio_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_uart_func(struct otg_instance *otg, u8 flag);
+void mxc_mc13783_mono_func(struct otg_instance *otg, u8 flag);
+int mxc_mc13783_mod_init(struct otg_instance *);
+void mxc_mc13783_mod_exit(struct otg_instance *);
+void mxc_set_transceiver_mode(int);
+
+int mxc_iomux_gpio_mc13783_set(int);
+int mxc_iomux_gpio_mc13783_reset(void);
+
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+struct tcd_ops tcd_ops;
+/*!
+ * mc13783_tcd_global_init() - non c99 global initializer
+ */
+void mc13783_tcd_global_init(void)
+{
+ ZERO(tcd_ops);
+
+ tcd_ops.id = mxc_mc13783_id;
+ tcd_ops.vbus = mxc_mc13783_vbus;
+ //tcd_ops.tcd_init_func = mx2_tcd_init;
+
+ tcd_ops.tcd_init_func = mxc_mc13783_tcd_init;
+ tcd_ops.tcd_en_func = mxc_mc13783_tcd_en;
+ tcd_ops.chrg_vbus_func = mxc_mc13783_chrg_vbus;
+ tcd_ops.drv_vbus_func = mxc_mc13783_drv_vbus;
+ tcd_ops.dischrg_vbus_func = mxc_mc13783_dischrg_vbus;
+ tcd_ops.dp_pullup_func = mxc_mc13783_dp_pullup_func;
+ tcd_ops.dm_pullup_func = mxc_mc13783_dm_pullup_func;
+ tcd_ops.dp_pulldown_func = mxc_mc13783_dp_pulldown_func;
+ tcd_ops.dm_pulldown_func = mxc_mc13783_dm_pulldown_func;
+
+ tcd_ops.overcurrent_func = NULL;
+ tcd_ops.dm_det_func = mxc_mc13783_dm_det_func;
+ tcd_ops.dp_det_func = mxc_mc13783_dp_det_func;
+ tcd_ops.cr_det_func = mxc_mc13783_cr_det_func;
+ //tcd_ops.charge_pump_func = mx2_charge_pump_func;
+ //tcd_ops.bdis_acon_func = mxc_mc13783_bdis_acon_func;
+ tcd_ops.peripheral_host_func = mxc_mc13783_peripheral_host_func;
+ tcd_ops.mx21_vbus_drain_func = mxc_mc13783_mx21_vbus_drain_func;
+ tcd_ops.id_pulldown_func = mxc_mc13783_id_pulldown_func;
+ tcd_ops.audio_func = mxc_mc13783_audio_func;
+ tcd_ops.uart_func = mxc_mc13783_uart_func;
+ tcd_ops.mono_func = mxc_mc13783_mono_func;
+
+ tcd_ops.mod_init = mc13783_tcd_mod_init;
+ tcd_ops.mod_exit = mc13783_tcd_mod_exit;
+}
+#else /* !defined(OTG_C99) */
+struct tcd_ops tcd_ops = {
+
+ //.id = mxc_mc13783_id,
+ //.vbus = mxc_mc13783_vbus,
+ //.tcd_init_func = mx2_tcd_init,
+
+ .tcd_init_func = mxc_mc13783_tcd_init,
+ .tcd_en_func = mxc_mc13783_tcd_en,
+ .chrg_vbus_func = mxc_mc13783_chrg_vbus,
+ .drv_vbus_func = mxc_mc13783_drv_vbus,
+ .dischrg_vbus_func = mxc_mc13783_dischrg_vbus,
+ .dp_pullup_func = mxc_mc13783_dp_pullup_func,
+ .dm_pullup_func = mxc_mc13783_dm_pullup_func,
+ .dp_pulldown_func = mxc_mc13783_dp_pulldown_func,
+ .dm_pulldown_func = mxc_mc13783_dm_pulldown_func,
+
+ .overcurrent_func = NULL,
+ .dm_det_func = mxc_mc13783_dm_det_func,
+ .dp_det_func = mxc_mc13783_dp_det_func,
+ .cr_det_func = mxc_mc13783_cr_det_func,
+ //.charge_pump_func = mx2_charge_pump_func,
+ //tcd_ops.bdis_acon_func = mxc_mc13783_bdis_acon_func,
+ .peripheral_host_func = mxc_mc13783_peripheral_host_func,
+ .mx21_vbus_drain_func = mxc_mc13783_mx21_vbus_drain_func,
+ .id_pulldown_func = mxc_mc13783_id_pulldown_func,
+ .audio_func = mxc_mc13783_audio_func,
+ .uart_func = mxc_mc13783_uart_func,
+ .mono_func = mxc_mc13783_mono_func,
+
+ .mod_init = mc13783_tcd_mod_init,
+ .mod_exit = mc13783_tcd_mod_exit,
+};
+#endif /* !defined(OTG_C99) */
+
+/* ********************************************************************************************* */
+void mc13783_tcd_mod_exit(struct otg_instance *otg);
+
+/*!
+ * mc13783_tcd_mod_init() - initial tcd setup
+ * This performs the platform specific hardware setup for the MX2ADS.
+ * @param otg - otg_instance pointer
+ *
+ */
+int mc13783_tcd_mod_init(struct otg_instance *otg)
+{
+ int gpio = 1;
+ bool res;
+ unsigned int reg_value;
+ int i;
+
+#if 1
+#ifdef CONFIG_OTG_ZASEVB_DIFFERENTIAL_BIDIRECTIONAL
+ int hwmode = XCVR_D_D;
+ int newmode = XCVR_D_D;
+#elif CONFIG_OTG_ZASEVB_DIFFERENTIAL_UNIDIRECTIONAL
+ int hwmode = XCVR_D_SE0_NEW;
+ int newmode = XCVR_D_D;
+#elif CONFIG_OTG_ZASEVB_SINGLE_ENDED_UNIDIRECTIONAL
+ int hwmode = XCVR_SE0_D_NEW;
+ int newmode = XCVR_SE0_D_NEW;
+#elif CONFIG_OTG_ZASEVB_SINGLE_ENDED_BIDIRECTIONAL
+ int hwmode = XCVR_SE0_SE0;
+ int newmode = XCVR_SE0_SE0;
+#else
+#error Please Configure Transceiver Mode
+#endif /* CONFIG_OTG_ZASEVB_.... */
+#endif
+
+ printk(KERN_INFO "%s: AAAA22\n", __FUNCTION__);
+
+ TRACE_MSG0(REMOVE_TCD, "1. mc13783 Connectivity");
+
+ mxc_mc13783_mod_init(otg);
+
+ TRACE_MSG0(REMOVE_TCD, "2. Transceiver setup");
+
+ switch (hwmode) {
+ case XCVR_D_D:
+ case XCVR_SE0_D_NEW:
+ case XCVR_D_SE0_NEW:
+ break;
+
+ case XCVR_SE0_SE0:
+ // this works with XCVR_SE0_SE0 if AP_GPIO_AP_C16 not configured
+ //isp1301_configure(dat_se0_bidirectional, spd_susp_reg); // XCVR_SEO_SE0
+ // XXX configure mc13783 transceiver here
+ break;
+ }
+
+ //isp1301_configure(vp_vm_bidirectional, spd_susp_reg); // XCVR_D_D
+
+ //TRACE_MSG0(REMOVE_TCD, "5. SET TCD OPS");
+ //THROW_UNLESS(mc13783_tcd_instance = otg_set_tcd_ops(otg, &tcd_ops), error);
+
+ mxc_iomux_gpio_mc13783_set(hwmode);
+
+ switch (hwmode) {
+ case XCVR_D_SE0_NEW:
+ TRACE_MSG0(REMOVE_TCD, "D_D - vp_vm_bidirectional");
+ printk(KERN_INFO "%s: D_D - Differential Unidirectional\n",
+ __FUNCTION__);
+ mc13783_convity_set_single_ended_mode(FALSE);
+ mc13783_convity_set_directional_mode(FALSE);
+ break;
+ case XCVR_SE0_D_NEW:
+ TRACE_MSG0(REMOVE_TCD, "SE0_D");
+ printk(KERN_INFO "%s: SE0_D - Single Ended Unidirectional\n",
+ __FUNCTION__);
+ mc13783_convity_set_single_ended_mode(TRUE);
+ mc13783_convity_set_directional_mode(FALSE);
+ break;
+ case XCVR_D_D:
+ TRACE_MSG0(REMOVE_TCD, "D_SE0");
+ printk(KERN_INFO "%s: D_SE0 - Differential Bidirectional\n",
+ __FUNCTION__);
+ mc13783_convity_set_single_ended_mode(FALSE);
+ mc13783_convity_set_directional_mode(TRUE);
+ break;
+
+ case XCVR_SE0_SE0:
+ TRACE_MSG0(REMOVE_TCD, "SE0_SE0 - SEO_bidirectional");
+ printk(KERN_INFO "%s: SE0_SE0 - Single Ended Bidirectional\n",
+ __FUNCTION__);
+ mc13783_convity_set_single_ended_mode(TRUE);
+ mc13783_convity_set_directional_mode(TRUE);
+ break;
+ }
+
+ TRACE_MSG0(REMOVE_TCD, "7. SET HWMODE");
+ mxc_set_transceiver_mode(newmode);
+ mc13783_convity_set_var_disconnect(TRUE); // variable 1k5 and UDP/UDM pull-down are disconnected. (PULLOVER)
+ mc13783_convity_set_usb_transceiver(TRUE); //USB transceiver is disabled (USBXCVREN)
+ mc13783_convity_set_udp_auto_connect(FALSE); //variable UDP is not automatically connected (SE0CONN)
+ mc13783_convity_set_pull_down_switch(PD_UDP_150, FALSE); //150K UDP pull-up switch is out (DP150KPU)
+ mc13783_convity_set_udp_pull(FALSE); //1.5K UDP pull-up and USB xcver is controlled by SPI bits.(USBCNTRL)
+ mc13783_convity_set_output(TRUE, FALSE); //disable vbus
+ mc13783_convity_set_output(FALSE, FALSE); //disable vusb
+ mc13783_convity_set_output(FALSE, TRUE); //enable vusb
+
+#if 1
+
+ for (i = 48; i < 51; i++) {
+ mc13783_read_reg(PRIO_CONN, i, &reg_value);
+ printk(KERN_INFO "Register %d = %8X\n", i, reg_value);
+ }
+#endif
+
+ /* Success! */
+
+ TRACE_MSG0(REMOVE_TCD, "8. Success!");
+
+ CATCH(error) {
+ printk(KERN_INFO "%s: failed\n", __FUNCTION__);
+ //SHP
+ //UNLESS (gpio) gpio_free_irq (3, GPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+ TRACE_MSG0(REMOVE_TCD, "MX2_MOD_TCD_INIT FINISHED");
+ return 0;
+}
+
+/*!
+ * mc13783cd_mod_exit() - de-initialize
+ * This is called from mx2-ocd.c
+ * @param otg - otg_instance pointer
+ */
+void mc13783_tcd_mod_exit(struct otg_instance *otg)
+{
+ //struct otg_instance *otg = tcd_instance->otg;
+ TRACE_MSG0(REMOVE_TCD, "MX2_MOD_TCD_EXIT");
+
+ mxc_iomux_gpio_mc13783_reset();
+
+ mxc_mc13783_mod_exit(otg);
+ mc13783_tcd_instance = otg_set_tcd_ops(otg, NULL);
+
+}
+#endif /* CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY */
diff --git a/drivers/otg/hardware/zasevb-pmic.c b/drivers/otg/hardware/zasevb-pmic.c
new file mode 100644
index 000000000000..23aefa2aa83d
--- /dev/null
+++ b/drivers/otg/hardware/zasevb-pmic.c
@@ -0,0 +1,318 @@
+/*
+ * 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
+ */
+/*
+ * otg/hardware/zasevb-mc13783.c -- Freescale mc13783 Connectivity driver
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/platform/zasevb/zasevb-pmic.c|20070614183950|37596
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/hardware/zasevb-pmic.c
+ * @brief mc13783 USB Transceiver Driver
+ *
+ * This is a wrapper for the Freescale MC13783 driver to support it on the ZASEVB.
+ *
+ * Notes
+ *
+ * 1. Ensure that S2.1 is ON and S2.2 is OFF.
+ *
+ * 2. Note that this driver has not been tested on the ZAS EVB.
+ *
+ * 3. The MC13783 Transceiver and USBOTG HWMODE must both be setup to match. Currently it
+ * appears that only the SEO-SEO / DAT-Bidirectional combination works correctly.
+ *
+ * @ingroup ZASEVB
+ * @ingroup TCD
+ *
+ */
+
+#include <asm/delay.h>
+
+#include <otg/pcd-include.h>
+#if defined(CONFIG_OTG_ZASEVB_MC13783_PMIC) || defined(_OTG_DOXYGEN)
+
+#include <asm/arch/gpio.h>
+
+#include "mxc-lnx.h"
+#include "mxc-hardware.h"
+
+#include "isp1301.h"
+#include "isp1301-hardware.h"
+
+/*
+ * These files are currently located in
+ * drivers/mxc/pmic
+ */
+#include <asm/arch/pmic_convity.h> /* For PMIC Connectivity driver interface. */
+#include <asm/arch/pmic_external.h> /* For PMIC Protocol driver interface. */
+//#include <core/pmic_event.h>
+
+
+
+/* ********************************************************************************************* */
+extern PMIC_CONVITY_HANDLE pmic_handle;
+extern int pmic_tcd_mod_init(struct otg_instance *otg);
+extern void pmic_tcd_mod_exit(struct otg_instance *otg);
+struct tcd_instance *pmic_tcd_instance;
+/* ********************************************************************************************* */
+int mxc_pmic_vbus(struct otg_instance *otg);
+int mxc_pmic_id(struct otg_instance *otg);
+void mxc_pmic_tcd_en(struct otg_instance *otg, u8 flag);
+void mxc_pmic_tcd_init(struct otg_instance *otg, u8 flag);
+void mxc_pmic_chrg_vbus(struct otg_instance *otg, u8 flag);
+void mxc_pmic_drv_vbus(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dischrg_vbus(struct otg_instance *otg, u8 flag);
+void mxc_pmic_mx21_vbus_drain_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dp_pullup_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dm_pullup_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dp_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dm_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_peripheral_host_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dm_det_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_dp_det_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_cr_det_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_bdis_acon_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_id_pulldown_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_audio_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_uart_func(struct otg_instance *otg, u8 flag);
+void mxc_pmic_mono_func(struct otg_instance *otg, u8 flag);
+int mxc_pmic_mod_init(struct otg_instance *);
+void mxc_pmic_mod_exit(struct otg_instance *);
+void mxc_set_transceiver_mode(int);
+
+int mxc_iomux_gpio_mc13783_set(int);
+int mxc_iomux_gpio_mc13783_reset(void);
+
+/* ********************************************************************************************* */
+#if !defined(OTG_C99)
+struct tcd_ops tcd_ops;
+/*!
+ * pmic_tcd_global_init() - non c99 global initializer
+ */
+void pmic_tcd_global_init(void)
+{
+ ZERO(tcd_ops);
+
+ tcd_ops.id = mxc_pmic_id;
+ tcd_ops.vbus = mxc_pmic_vbus;
+ //tcd_ops.tcd_init_func = mx2_tcd_init;
+
+ tcd_ops.tcd_init_func = mxc_pmic_tcd_init;
+ tcd_ops.tcd_en_func = mxc_pmic_tcd_en;
+ tcd_ops.chrg_vbus_func = mxc_pmic_chrg_vbus;
+ tcd_ops.drv_vbus_func = mxc_pmic_drv_vbus;
+ tcd_ops.dischrg_vbus_func = mxc_pmic_dischrg_vbus;
+ tcd_ops.dp_pullup_func = mxc_pmic_dp_pullup_func;
+ tcd_ops.dm_pullup_func = mxc_pmic_dm_pullup_func;
+ tcd_ops.dp_pulldown_func = mxc_pmic_dp_pulldown_func;
+ tcd_ops.dm_pulldown_func = mxc_pmic_dm_pulldown_func;
+
+ tcd_ops.overcurrent_func = NULL;
+ tcd_ops.dm_det_func = mxc_pmic_dm_det_func;
+ tcd_ops.dp_det_func = mxc_pmic_dp_det_func;
+ tcd_ops.cr_det_func = mxc_pmic_cr_det_func;
+ //tcd_ops.charge_pump_func = mx2_charge_pump_func;
+ //tcd_ops.bdis_acon_func = mxc_pmic_bdis_acon_func;
+ tcd_ops.peripheral_host_func = mxc_pmic_peripheral_host_func;
+ tcd_ops.mx21_vbus_drain_func = mxc_pmic_mx21_vbus_drain_func;
+ tcd_ops.id_pulldown_func = mxc_pmic_id_pulldown_func;
+ tcd_ops.audio_func = mxc_pmic_audio_func;
+ tcd_ops.uart_func = mxc_pmic_uart_func;
+ tcd_ops.mono_func = mxc_pmic_mono_func;
+
+ tcd_ops.mod_init = pmic_tcd_mod_init;
+ tcd_ops.mod_exit = pmic_tcd_mod_exit;
+}
+#else /* !defined(OTG_C99) */
+struct tcd_ops tcd_ops = {
+
+ //.id = mxc_pmic_id,
+ //.vbus = mxc_pmic_vbus,
+ //.tcd_init_func = mx2_tcd_init,
+
+ .tcd_init_func = mxc_pmic_tcd_init,
+ .tcd_en_func = mxc_pmic_tcd_en,
+ .chrg_vbus_func = mxc_pmic_chrg_vbus,
+ .drv_vbus_func = mxc_pmic_drv_vbus,
+ .dischrg_vbus_func = mxc_pmic_dischrg_vbus,
+ .dp_pullup_func = mxc_pmic_dp_pullup_func,
+ .dm_pullup_func = mxc_pmic_dm_pullup_func,
+ .dp_pulldown_func = mxc_pmic_dp_pulldown_func,
+ .dm_pulldown_func = mxc_pmic_dm_pulldown_func,
+
+ .overcurrent_func = NULL,
+ .dm_det_func = mxc_pmic_dm_det_func,
+ .dp_det_func = mxc_pmic_dp_det_func,
+ .cr_det_func = mxc_pmic_cr_det_func,
+ //.charge_pump_func = mx2_charge_pump_func,
+ //tcd_ops.bdis_acon_func = mxc_pmic_bdis_acon_func,
+ .peripheral_host_func = mxc_pmic_peripheral_host_func,
+ .mx21_vbus_drain_func = mxc_pmic_mx21_vbus_drain_func,
+ .id_pulldown_func = mxc_pmic_id_pulldown_func,
+ .audio_func = mxc_pmic_audio_func,
+ .uart_func = mxc_pmic_uart_func,
+ .mono_func = mxc_pmic_mono_func,
+
+ .mod_init = pmic_tcd_mod_init,
+ .mod_exit = pmic_tcd_mod_exit,
+};
+#endif /* !defined(OTG_C99) */
+
+/* ********************************************************************************************* */
+void pmic_tcd_mod_exit(struct otg_instance *otg);
+
+/*!
+ * pmic_tcd_mod_init() - initial tcd setup
+ * This performs the platform specific hardware setup for the MX2ADS.
+ * @param otg - otg_instance pointer
+ *
+ */
+int pmic_tcd_mod_init(struct otg_instance *otg)
+{
+ int gpio = 1;
+ bool res;
+ unsigned int reg_value, reg_mask = 0xfffff;
+ int i;
+#if 1
+#ifdef CONFIG_OTG_ZASEVB_DIFFERENTIAL_BIDIRECTIONAL
+ int hwmode = XCVR_D_D;
+ int newmode = XCVR_D_D;
+#elif CONFIG_OTG_ZASEVB_DIFFERENTIAL_UNIDIRECTIONAL
+ int hwmode = XCVR_D_SE0_NEW;
+ int newmode = XCVR_D_D;
+#elif CONFIG_OTG_ZASEVB_SINGLE_ENDED_UNIDIRECTIONAL
+ int hwmode = XCVR_SE0_D_NEW;
+ int newmode = XCVR_SE0_D_NEW;
+#elif CONFIG_OTG_ZASEVB_SINGLE_ENDED_BIDIRECTIONAL
+ int hwmode = XCVR_SE0_SE0;
+ int newmode = XCVR_SE0_SE0;
+#else
+#error Please Configure Transceiver Mode
+#endif /* CONFIG_OTG_ZASEVB_.... */
+#endif
+
+ printk(KERN_INFO "%s: AAAA22\n", __FUNCTION__);
+
+ TRACE_MSG0(REMOVE_TCD, "1. mc13783 Connectivity");
+
+ mxc_pmic_mod_init(otg);
+
+ TRACE_MSG0(REMOVE_TCD, "2. Transceiver setup");
+
+ switch (hwmode) {
+ case XCVR_D_D:
+ case XCVR_SE0_D_NEW:
+ case XCVR_D_SE0_NEW:
+ break;
+
+ case XCVR_SE0_SE0:
+ // this works with XCVR_SE0_SE0 if AP_GPIO_AP_C16 not configured
+ //isp1301_configure(dat_se0_bidirectional, spd_susp_reg); // XCVR_SEO_SE0
+ // XXX configure mc13783 transceiver here
+ break;
+ }
+
+ //isp1301_configure(vp_vm_bidirectional, spd_susp_reg); // XCVR_D_D
+
+ //TRACE_MSG0(REMOVE_TCD, "5. SET TCD OPS");
+ //THROW_UNLESS(mc13783_tcd_instance = otg_set_tcd_ops(otg, &tcd_ops), error);
+
+ mxc_iomux_gpio_mc13783_set(hwmode);
+
+ switch (hwmode) {
+ case XCVR_D_SE0_NEW:
+ TRACE_MSG0(REMOVE_TCD, "D_D - vp_vm_bidirectional");
+ printk(KERN_INFO "%s: D_D - Differential Unidirectional\n",
+ __FUNCTION__);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_OTG_SE0CONN);
+ pmic_convity_usb_set_xcvr(pmic_handle, USB_DIFFERENTIAL_UNIDIR);
+ break;
+ case XCVR_SE0_D_NEW:
+ TRACE_MSG0(REMOVE_TCD, "SE0_D");
+ printk(KERN_INFO "%s: SE0_D - Single Ended Unidirectional\n",
+ __FUNCTION__);
+ pmic_convity_usb_set_xcvr(pmic_handle, USB_SINGLE_ENDED_UNIDIR);
+ break;
+ case XCVR_D_D:
+ TRACE_MSG0(REMOVE_TCD, "D_SE0");
+ printk(KERN_INFO "%s: D_SE0 - Differential Bidirectional\n",
+ __FUNCTION__);
+ pmic_convity_usb_set_xcvr(pmic_handle, USB_DIFFERENTIAL_BIDIR);
+ break;
+
+ case XCVR_SE0_SE0:
+ TRACE_MSG0(REMOVE_TCD, "SE0_SE0 - SEO_bidirectional");
+ printk(KERN_INFO "%s: SE0_SE0 - Single Ended Bidirectional\n",
+ __FUNCTION__);
+ pmic_convity_usb_set_xcvr(pmic_handle, USB_SINGLE_ENDED_BIDIR);
+ break;
+ }
+
+ TRACE_MSG0(REMOVE_TCD, "7. SET HWMODE");
+
+ mxc_set_transceiver_mode(newmode);
+ pmic_convity_usb_otg_set_config(pmic_handle, USB_PULL_OVERRIDE); // variable 1k5 and UDP/UDM pull-down are disconnected. (PULLOVER)
+ pmic_convity_usb_otg_set_config(pmic_handle, USBXCVREN); //USB transceiver is disabled (USBXCVREN)
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_OTG_SE0CONN); //variable UDP is not automatically connected (SE0CONN)
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_DP150K_PU);
+ pmic_convity_usb_otg_clear_config(pmic_handle, USB_USBCNTRL);
+ pmic_convity_set_output(pmic_handle, TRUE, FALSE); //disable vbus TBD
+ pmic_convity_set_output(pmic_handle, FALSE, FALSE); //disable vusb TBD
+ pmic_convity_set_output(pmic_handle, FALSE, TRUE); //enable vusb TBD
+
+
+#if 1
+
+ for (i = 0; i < 3; i++) {
+ pmic_read_reg(REG_CHARGER + i, &reg_value, reg_mask);
+ printk(KERN_INFO "Register %d = %8X\n", REG_CHARGER + i, reg_value);
+ }
+#endif
+
+ /* Success! */
+
+ TRACE_MSG0(REMOVE_TCD, "8. Success!");
+
+ CATCH(error) {
+ printk(KERN_INFO "%s: failed\n", __FUNCTION__);
+ //SHP
+ //UNLESS (gpio) gpio_free_irq (3, GPIO_PIN, GPIO_HIGH_PRIO);
+ return -EINVAL;
+ }
+ TRACE_MSG0(REMOVE_TCD, "MX2_MOD_TCD_INIT FINISHED");
+ return 0;
+}
+
+/*!
+ * pmic_tcd_mod_exit() - de-initialize
+ * This is called from mx2-ocd.c
+ * @param otg - otg_instance pointer
+ */
+void pmic_tcd_mod_exit(struct otg_instance *otg)
+{
+ //struct otg_instance *otg = tcd_instance->otg;
+ TRACE_MSG0(REMOVE_TCD, "MX2_MOD_TCD_EXIT");
+
+ mxc_iomux_gpio_mc13783_reset();
+
+ mxc_pmic_mod_exit(otg);
+ pmic_tcd_instance = otg_set_tcd_ops(otg, NULL);
+
+}
+#endif /* CONFIG_OTG_ZASEVB_MC13783_CONNECTIVITY */
diff --git a/drivers/otg/otg-config-std.h b/drivers/otg/otg-config-std.h
new file mode 100644
index 000000000000..a6ae61785ba6
--- /dev/null
+++ b/drivers/otg/otg-config-std.h
@@ -0,0 +1,95 @@
+/*
+ * 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
+ */
+/*
+ * @(#) balden@belcarra.com|otg/otg-config-std.h|20060419204305|26216
+ *
+ */
+
+
+
+/*
+ * config OTG_USB_PERIPHERAL
+ * bool "USB Peripheral (only)"
+ * Compile as a standard USB Peripheral.
+ */
+/* #define OTG_USB_PERIPHERAL */
+
+/*
+ * config OTG_USB_HOST
+ * bool "USB Host (only)"
+ * Compile as a standard USB Host.
+ */
+/* #define OTG_USB_HOST */
+
+/*
+ * config OTG_USB_PERIPHERAL_OR_HOST
+ * bool "USB Peripheral or Host"
+ * Compile as a standard USB Peripheral and Host.
+ * The transceiver driver must implement ID_GND to switch between
+ * host and peripheral roles.
+ */
+/* #define OTG_USB_PERIPHERAL_OR_HOST */
+
+/*
+ * config OTG_BDEVICE_WITH_SRP
+ * bool "SRP Capable B-Device (Only)"
+ * Compile as a On-The-Go Peripheral-Only SRP capable device. This
+ * is similiar to a Traditional USB Peripheral but enables
+ * On-The-Go features such as SRP.
+ */
+/* #define OTG_BDEVICE_WITH_SRP */
+
+/*
+ * config OTG_DEVICE
+ * bool "OTG Device - can act as A or B Device"
+ * Implement full On-The-Go Device support for a platform that
+ * supports implemenation of A and B Device.
+ */
+/* #define OTG_DEVICE */
+
+
+/*
+ * bool 'OTG Fast Tracing'
+ * This option implements register trace to support
+ * driver debugging.
+ */
+/* #define CONFIG_OTG_TRACE */
+
+
+
+/*
+ * bool 'Built-in Minimal USB Device'
+ * Compile in minimal USB Device only firmware.
+ */
+/* #define CONFIG_OTG_FW_MN */
+
+/*
+ * bool 'Enable Auto-Start'
+ * Automatically start and enable minimal USB Device.
+ */
+/* #define CONFIG_OTG_TR_AUTO */
+
+/*
+ * bool 'Disable C99 initializers'
+ * If your compiler does not allow a structure to be initialized as .element_name=value
+ */
+/* #define CONFIG_OTG_NOC99 */
+
+/*
+ * boolean "USB Host - OTG Support"
+ * 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.
+ */
+/* #define CONFIG_USB_OTG */
diff --git a/drivers/otg/otg/hcd-hw.h b/drivers/otg/otg/hcd-hw.h
new file mode 100644
index 000000000000..0e110dda055f
--- /dev/null
+++ b/drivers/otg/otg/hcd-hw.h
@@ -0,0 +1,114 @@
+/*
+ * 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
+ */
+/*
+ * otg/hcd/hc_xfer_hw.h - Generic transfer level USBOTG aware Host Controller Driver (HCD)
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/hcd-hw.h|20061017072623|49375
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/hcd-hw.h
+ * @brief Implements Generic Host Controller Driver
+ *
+ * @ingroup HCD
+ */
+#ifndef HC_XFER_HW_H
+#define HC_XFER_HW_H 1
+
+/*===========================================================================*
+ * Functions provided by the hardware specific component (whatever HW it is).
+ * Each HW specific component provides these functions.
+ *===========================================================================*/
+
+/*===========================================================================*
+ * For the generic transfer aware framework.
+ *===========================================================================*/
+
+/*
+ * hcd_hw_max_active_transfers()
+ * Returns: the (constant) maximum number of concurrently active transfers the HW will handle.
+ */
+extern int hcd_hw_max_active_transfers(struct bus_hcpriv *bus_hcpriv);
+
+/*
+ * hcd_hw_start_transfer()
+ * Returns: tid >= 0 when the transfer starts - this is an index in [0..hcd_hw_max_active_transfers]
+ * -1 when the transfer is invalid,
+ * -2 when there are no resources for the transfer currently available.
+ */
+extern int hcd_hw_start_transfer(struct bus_hcpriv *bus_hcpriv,
+ int len, // length of data region
+ void *data, // virtual address of data region
+ int toggle, // toggle value to start the xfer with
+ int maxps, // max packet size of endpoint
+ int slow, // 1 ==> slow speed, 0 ==> full speed QQSV verify: (((urb->pipe) >> 26) & 1)
+ int endpoint, // endpoint number
+ int address, // USB device address
+ int pid, // {PID_OUT, PID_IN, PID_SETUP}
+ int format, // PIPE_{CONTROL,BULK,INTERRUPT,ISOCHRONOUS}
+ u32 other); // Values depending on format
+
+/*
+ * hcd_hw_unlink_urb()
+ * Called in irq_lock, calls back to xhc_transfer_complete().
+ * Returns: 0 when transfer is unlinked, non-zero otherwise.
+ */
+extern int hcd_hw_unlink_urb(struct bus_hcpriv *bus_hcpriv, int tid); // tid is value returned by hcd_hw_start_transfer()
+
+extern u32 hcd_hw_frame_number(struct bus_hcpriv *bus_hcpriv);
+
+extern void hcd_hw_enable_interrupts(struct bus_hcpriv *bus_hcpriv);
+extern void hcd_hw_disable_interrupts(struct bus_hcpriv *bus_hcpriv);
+
+extern void hcd_hw_get_ops(struct bus_hcpriv *bus_hcpriv);
+
+extern int hcd_hw_init(struct bus_hcpriv *bus_hcpriv);
+
+extern void hcd_hw_exit(struct bus_hcpriv *bus_hcpriv);
+
+/*===========================================================================*
+ * For the virtual root hub.
+ *===========================================================================*/
+
+extern int hcd_hw_rh_num_ports(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_otg_capable_mask(struct bus_hcpriv *bus_hcpriv);
+
+extern char *hcd_hw_rh_string(struct bus_hcpriv *bus_hcpriv, int strno);
+extern u16 hcd_hw_rh_idVendor(struct bus_hcpriv *bus_hcpriv);
+extern u16 hcd_hw_rh_idProduct(struct bus_hcpriv *bus_hcpriv);
+extern u16 hcd_hw_rh_bcdDevice(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_cfg_bmAttributes(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_cfg_MaxPower(struct bus_hcpriv *bus_hcpriv);
+extern u16 hcd_hw_rh_hub_attributes(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_power_delay(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_hub_contr_current(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_DeviceRemovable(struct bus_hcpriv *bus_hcpriv);
+extern u8 hcd_hw_rh_PortPwrCtrlMask(struct bus_hcpriv *bus_hcpriv);
+extern u32 hcd_hw_rh_get_hub_change_status(struct bus_hcpriv *bus_hcpriv);
+extern void hcd_hw_rh_hub_feature(struct bus_hcpriv *bus_hcpriv, int feat_selector, int set_flag);
+extern void hcd_hw_hcd_en_func(struct otg_instance *oi, u8 on);
+/*
+ * Note: in the following functions, portnum is 0-origin.
+ * (I.e., valid range is [0..(hcd_hw_rh_num_ports-1)].)
+ */
+extern u32 hcd_hw_rh_get_port_change_status(struct bus_hcpriv *bus_hcpriv, int portnum);
+extern void hcd_hw_rh_port_feature(struct bus_hcpriv *bus_hcpriv, u16 wValue, u16 wIndex, int set_flag);
+
+
+#endif
diff --git a/drivers/otg/otg/hcd-l26.h b/drivers/otg/otg/hcd-l26.h
new file mode 100644
index 000000000000..c11502ce969c
--- /dev/null
+++ b/drivers/otg/otg/hcd-l26.h
@@ -0,0 +1,163 @@
+/*
+ * 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
+ */
+/*
+ * otg/hcd/hc_xfer.h - Generic transfer level USBOTG aware Host Controller Driver (HCD)
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/hcd-l26.h|20061017072623|12456
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/hcd-l26.h
+ * @brief Implements Linux version of Generic Host Controller driver
+ *
+ * @ingroup HCD
+ */
+#ifndef HC_XFER_H
+#define HC_XFER_H 1
+
+/*===========================================================================*
+ * Data structures and functions provided by the generic transfer aware
+ * host controller framework.
+ *
+ * The generic framework must be linked with a hardware specific component,
+ * whose functions are specified in hc_xfer_hw.h, and a generic root hub
+ * component, whose functions are specified in hc_xfer_rh.h.
+ *
+ * The resulting module provides a table of operations to the usb core layer,
+ * passed to the core layer by a call to usb_alloc_bus().
+ *===========================================================================*/
+
+/*! @name Descriptor sizes per descriptor type
+ */
+ /*! @{ */
+
+// XXX this should be fixed....
+
+#define USB_DT_DEVICE_SIZE 18
+#define USB_DT_CONFIG_SIZE 9
+#define USB_DT_INTERFACE_SIZE 9
+#define USB_DT_ENDPOINT_SIZE 7
+#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE 7
+#define USB_DT_HID_SIZE 9
+/*! @} */
+
+
+#define XHC_RH_DESCRIPTORS_SIZE (USB_DT_DEVICE_SIZE+USB_DT_CONFIG_SIZE+USB_DT_INTERFACE_SIZE+USB_DT_ENDPOINT_SIZE+USB_DT_HUB_NONVAR_SIZE+2)
+
+/*! typedef struct rh_hcpriv rh_hcpriv_t
+ *@brief - encapsulation of root hub relatied variables
+ */
+
+typedef struct rh_hcpriv {
+ struct urb *int_urb;
+ struct usbd_device_descriptor *dd;
+ struct usbd_configuration_descriptor *cd;
+ struct usbd_interface_descriptor *id;
+ struct usbd_endpoint_descriptor *ed;
+ struct hub_descriptor *hd;
+ struct timer_list poll_timer;
+ //int poll_jiffies;
+ struct usb_device *dev;
+ int dev_registered;
+ u32 hub_change_status; // For shadowing the HW
+ u32 *port_change_status; // An array of [num_ports] values for shadowing the HW
+ u32 hub_port_change_status;
+ struct WORK_STRUCT psc_bh;
+ u8 curr_cfg;
+ u8 curr_itf;
+ //u8 num_ports;
+ u8 otg_device_mask; // (1 << port_num) bit set iff port_num is acting as a device, not host, and so not usable
+ u8 descriptors[XHC_RH_DESCRIPTORS_SIZE];
+ int suspended;
+} rh_hcpriv_t;
+
+/*! typedef struct bus_hcpriv bus_hcpriv_t
+ * @brief -encapsualtion of bus related host controller variables
+ */
+
+typedef struct bus_hcpriv { // XFER level Host Controller Info
+ struct usb_bus *usb_bus;
+#if 1 //SHP
+ struct usb_hcd hcd;
+#endif
+ void *hw_hci; // HW specific information for this HC
+ int root_hub_addr; // Initially 0, set by the virtual root hub when addressed.
+ struct rh_hcpriv *rh_hcpriv; // Generic root hub info (see hc_xfer_rh.[hc])
+ int terminating; // Boolean, TRUE if shutting down.
+ //struct hcd_instance *hcd; // OTG Controller Info
+ int max_active_urbs;
+ struct urb **active_urbs;
+ int device_registered; // bus_device status
+ struct device bus_device; // Linux Driver Model info.
+ //struct hcd_ops otg_ops; // operations for OTG component.
+ struct usb_driver *usb_driver;
+
+ u8 num_ports;
+ u8 otg_port;
+ u8 otg_capable_mask;
+ u16 rh_vendorid;
+ u16 rh_productid;
+ u16 rh_bcddevice;
+ char *rh_serial;
+ char *rh_product;
+ char *rh_manufacturer;
+ u8 rh_bmAttributes;
+ u8 rh_bMaxPower;
+
+ struct usb_device *roothub_dev;
+ struct usb_device *first_dev;
+
+ int max_active_transfers;
+
+} bus_hcpriv_t;
+
+/*! typedef struct dev_hcpriv dev_hcpriv_t
+ * @brief encapsulation about device related host controller globals
+ */
+
+typedef struct dev_hcpriv { // XFER level HCI per attached device info
+ struct usb_device *dev;
+ struct list_head queued_urbs_both[2][16];
+ u32 num_urbs_both[2][16];
+ u8 epq_state_both[2][16];
+} dev_hcpriv_t;
+
+#define EPQ_EMPTY 0
+#define EPQ_RUNNING 1
+#define EPQ_WAITING 2
+
+/*===========================================================================*
+ * For the hardware specific component.
+ *===========================================================================*/
+
+extern void hcd_transfer_complete(struct bus_hcpriv *bus_hcpriv, int transfer_id, int format,
+ int cc, u32 remaining, int next_toggle);
+
+/*===========================================================================*
+ * For the virtual root hub.
+ *===========================================================================*/
+
+extern void hcd_rh_urb_complete(struct bus_hcpriv *bus_hcpriv, struct urb *urb);
+
+#include "../otg/otg-trace.h"
+extern otg_tag_t xfer_hci_trace_tag;
+
+
+#endif
diff --git a/drivers/otg/otg/hcd-rh.h b/drivers/otg/otg/hcd-rh.h
new file mode 100644
index 000000000000..86248785a7e6
--- /dev/null
+++ b/drivers/otg/otg/hcd-rh.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+/*
+ * otg/hcd/hc_xfer_rh.h - Generic transfer level USBOTG aware Host Controller Driver (HCD)
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/hcd-rh.h|20061017072623|12102
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/hcd-rh.h
+ * @brief Implements Generic Root Hub function for Generic Host Controller Driver.
+ *
+ * @ingroup HCD
+ */
+#ifndef HC_XFER_RH_H
+#define HC_XFER_RH_H 1
+
+#define XHC_RH_STRING_CONFIGURATION 1
+#define XHC_RH_STRING_INTERFACE 2
+#define XHC_RH_STRING_SERIAL 3
+#define XHC_RH_STRING_PRODUCT 4
+#define XHC_RH_STRING_MANUFACTURER 5
+
+/*===========================================================================*
+ * Functions provided by the generic virtual root hub.
+ *===========================================================================*/
+
+/*===========================================================================*
+ * For the generic transfer aware framework.
+ *===========================================================================*/
+
+/*
+ * hcd_rh_submit_urb()
+ * Returns: values as for the generic submit_urb() function.
+ */
+extern int hcd_rh_submit_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb, int mem_flags);
+
+extern int hcd_rh_unlink_urb(struct bus_hcpriv *bus_hcpriv, struct urb *urb);
+
+extern void hcd_rh_get_ops(struct bus_hcpriv *bus_hcpriv);
+
+extern int hcd_rh_init(struct bus_hcpriv *bus_hcpriv);
+
+extern void hcd_rh_exit(struct bus_hcpriv *bus_hcpriv);
+
+/*===========================================================================*
+ * For the hardware specific root hub component.
+ *===========================================================================*/
+
+extern irqreturn_t hcd_rh_int_hndlr(int irq, void *dev_id, struct pt_regs *regs);
+
+#endif
diff --git a/drivers/otg/otg/otg-api.h b/drivers/otg/otg/otg-api.h
new file mode 100644
index 000000000000..8742296058a8
--- /dev/null
+++ b/drivers/otg/otg/otg-api.h
@@ -0,0 +1,375 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg-api.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-api.h|20070125083535|47004
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*
+ * Doxygen group definitions - N.B. These much each be in
+ * their own separate comment...
+ */
+
+/*!
+ * @defgroup FunctionDrivers USB Function Drivers
+ * @brief These are the USB Function Drivers for Belcarra USBOTG.
+ */
+ /*!
+ * @defgroup CompositeFunctions Composite USB Functions
+ * @ingroup FunctionDrivers
+ * @brief These are the USB Composite Function Drivers for Belcarra USBOTG.
+ */
+ /*!
+ * @defgroup InterfaceFunctions Interface USB Functions
+ * @ingroup FunctionDrivers
+ * @brief These are the USB Interface Function Drivers for Belcarra USBOTG.
+ */
+ /*!
+ * @defgroup ClassFunctions Class USB Functions
+ * @ingroup FunctionDrivers
+ * @brief These are the USB Class Function Drivers for Belcarra USBOTG.
+ */
+ /*!
+ * @defgroup SimpleFunctions Simple USB Functions
+ * @ingroup FunctionDrivers
+ * @brief These are the USB Simple Function Drivers for Belcarra USBOTG.
+ */
+
+
+/*!
+ * @defgroup USBDOTG USBOTG
+ */
+ /*!
+ * @defgroup USBDAPI USBD Function Driver API
+ * @ingroup USBDOTG
+ * @brief This is the USBD API.
+ */
+ /*!
+ * @defgroup OTGAPI OTG Driver API
+ * @ingroup USBDOTG
+ * @brief This is the OTG State Machine API.
+ */
+ /*!
+ * @defgroup USBDCORE USBD Core
+ * @ingroup USBDOTG
+ * @brief This implements the USBD API.
+ */
+ /*!
+ * @defgroup OTGCORE OTG State Machine
+ * @ingroup USBDOTG
+ * @brief This implements the OTG State Machine API.
+ */
+ /*!
+ * @defgroup OTGINIT OTG Core Init
+ * @ingroup USBDOTG
+ * @brief This implements the OTG initialization.
+ */
+ /*!
+ * @defgroup OTGMESG OTG Core Mesg
+ * @ingroup USBDOTG
+ * @brief This implements the OTG messaging facility.
+ */
+ /*!
+ * @defgroup OTGTRACE OTG Core Trace
+ * @ingroup USBDOTG
+ * @brief This implements the OTG trace facility.
+ */
+#if 0
+ /*!
+ * @defgroup OTGFW OTG Firmware
+ * @ingroup USBDOTG
+ * @brief This implements the OTG State Machine Firmware.
+ */
+#endif
+/*!
+ * @defgroup OSPort OS Support
+ */
+ /*!
+ * @defgroup OSAPI OTG OS Support API
+ * @ingroup OSPort
+ * @brief This defines the OTG OS API.
+ */
+ /*!
+ * @defgroup LINUXAPI Linux OTG OS Support API
+ * @ingroup OSPort
+ * @brief This defines the Linux version of the OTG OS API.
+ */
+ /*!
+ * @defgroup LINUXOS Linux OTG OS Support
+ * @ingroup OSPort
+ * @brief This implements the Linux OTG OS API.
+ */
+
+/*!
+ * @defgroup Platform Platform
+ */
+/*!
+ * @defgroup Hardware Platform Specific Hardware
+ * @ingroup Platform
+ * @brief The Platform specific hardware drivers.
+ */
+ /*!
+ * @defgroup PCD Peripheral Controller Driver
+ * @ingroup Platform
+ * @brief The Peripheral Controller Driver controls the USB Device Controller hardware.
+ */
+ /*!
+ * @defgroup TCD Transceiver Controller Driver
+ * @ingroup Platform
+ * @brief The Transceiver Controller Driver controls the USB and/or OTG Transceiver Controller hardware.
+ */
+ /*!
+ * @defgroup HCD Host Controller Driver
+ * @ingroup Platform
+ * @brief The Host Controller Driver controls the USB Host Controller hardware.
+ */
+ /*!
+ * @defgroup OCD OTG Controller Driver
+ * @ingroup Platform
+ * @brief The OTG Controller Driver controls the OTG Transceiver Controller hardware that is common to
+ * both PCD and HCD.
+ */
+
+
+/*!
+ * @file otg/otg/otg-api.h
+ * @brief Core Defines for USB OTG Core Layaer
+ *
+ * @ingroup OTGAPI
+ */
+
+
+
+
+/*!
+ * @name OTGCORE OTG API Definitions
+ * This contains the OTG API structures and definitions.
+ * @{
+ */
+
+#include <otg/otg-fw.h>
+
+#if defined(CONFIG_OTG_USB_PERIPHERAL) || defined (CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)
+#include <otg/otg-fw-mn.h>
+#elif defined(CONFIG_OTG_BDEVICE_WITH_SRP) || defined(CONFIG_OTG_DEVICE)
+#include <otg/otg-fw-df.h>
+#else /* error */
+//#abort "Missing USB or OTG configuration"
+#endif
+
+
+struct otg_instance;
+typedef void (*otg_output_proc_t) (struct otg_instance *, u8);
+
+extern struct otg_firmware *otg_firmware_loaded;
+extern struct otg_firmware *otg_firmware_orig;
+extern struct otg_firmware *otg_firmware_loading;
+
+
+char * otg_get_state_name(int state);
+
+typedef u64 otg_current_t;
+
+/*!
+ * @struct otg_instance otg-api.h "otg/otg-api.h"
+ *
+ * This tracks the current OTG configuration and state
+ */
+struct otg_instance {
+
+ u16 state; /*!< current state */
+ int previous; /*!< previous state */
+
+ int active; /*!< used as semaphore */
+
+ u32 current_inputs; /*!< input settings */
+ otg_current_t outputs; /*!< output settings */
+
+ otg_tick_t tickcount; /*!< when we transitioned to current state */
+ otg_current_t bconn; /*!< when b_conn was set */
+
+ struct otg_state *current_outputs; /*!< output table entry for current state */
+ struct otg_state *previous_outputs; /*!< output table entry for current state */
+
+ otg_output_proc_t otg_output_ops[MAX_OUTPUTS]; /*!< array of functions mapped to output numbers */
+
+ int (*start_timer) (struct otg_instance *, int); /*!< OCD function to start timer */
+ otg_tick_t (*ticks) (void); /*!< OCD function to return ticks */
+ otg_tick_t (*elapsed) ( otg_tick_t *, otg_tick_t *); /*!< OCD function to return elapsed ticks */
+
+ struct hcd_instance* hcd; /*!< pointer to hcd instance */
+ struct ocd_instance* ocd; /*!< pointer to ocd instance */
+ struct pcd_instance* pcd; /*!< pointer to pcd instance */
+ struct tcd_instance* tcd; /*!< pointer to tcd instance */
+
+ struct hcd_ops* hcd_ops;
+ struct ocd_ops* ocd_ops;
+ struct pcd_ops* pcd_ops;
+ struct tcd_ops* tcd_ops;
+
+
+ char function_name[OTGADMIN_MAXSTR]; /*!< current function */
+ char serial_number[OTGADMIN_MAXSTR]; /*!< current serial number */
+
+ u32 interrupts; /*!< track number of interrupts */
+
+ //#if 0
+ struct otg_tasklet *tasklet;
+ //#else
+ struct otg_task *task;
+ //otg_sem_t event;
+ //#endif
+ otg_sem_t command;
+ struct otg_task *message_task;
+
+ otg_pthread_mutex_t mutex;
+
+ otg_tag_t TAG;
+
+ void *privdata;
+};
+
+typedef int (*otg_event_t) (struct otg_instance *, int, char *);
+/*!
+ * @struct otg_event_info otg-api.h "otg/otg-api.h"
+ */
+
+struct otg_event_info {
+ char *name; /*< otg event name */
+ otg_event_t event; /*< otg event */
+};
+
+//typedef u16 (*framenum_t)(struct otg_instance *);
+typedef u16 (*framenum_t)(struct otg_instance *otg);
+
+/* 1 - used by external functions to pass in administrative commands as simple strings
+ */
+extern int otg_status(int, u32, u32, char *, int);
+
+/* 2
+ */
+struct otg_instance *otg_create(void);
+void otg_destroy(struct otg_instance *otg);
+
+
+/* 3 - used internally by any OTG stack component to single event from non-interrupt context,
+ */
+extern void otg_queue_event(struct otg_instance *, otg_current_t, otg_tag_t, char *);
+extern void otg_event(struct otg_instance *, otg_current_t, otg_tag_t, char *);
+extern void otg_event_new(struct otg_instance *, otg_current_t, otg_tag_t, char *);
+
+/* 5 - used by PCD/TCD drivers to signal various OTG Transceiver events
+ */
+void otg_event_set_irq(struct otg_instance *, int, int, u32, otg_tag_t, char *);
+
+
+extern void otg_serial_number(struct otg_instance *otg, char *serial_number_str);
+extern void otg_init (struct otg_instance *otg);
+extern void otg_exit (struct otg_instance *otg);
+
+/* message
+ */
+#if defined(CONFIG_OTG_LNX)
+extern int otg_message_init_l24(struct otg_instance *);
+extern void otg_message_exit_l24(struct otg_instance *);
+#endif /* defined(OTG_LNX) */
+
+#if defined(CONFIG_OTG_QNX)
+extern int otg_message_init_qnx(struct otg_instance *);
+extern void otg_message_exit_qnx(struct otg_instance *);
+#endif /* defined(CONFIG_OTG_QNX) */
+
+extern int otg_message_init(struct otg_instance *);
+extern void otg_message_exit(void);
+
+//extern int otg_write_message_irq(char *buf, int size);
+extern int otg_write_message(struct otg_instance *otg, char *buf, int size);
+extern int otg_read_message(char *buf, int size);
+extern int otg_data_queued(void);
+extern unsigned int otg_message_block(void);
+//extern unsigned int otg_message_poll(struct file *, struct poll_table_struct *);
+
+
+/* trace
+ */
+extern int otg_trace_init (void);
+extern void otg_trace_exit (void);
+#if defined(OTG_LINUX)
+extern int otg_trace_init_l24(void);
+extern void otg_trace_exit_l24(void);
+#endif /* defined(OTG_LINUX) */
+
+#if defined(OTG_WINCE)
+extern int otg_trace_init_w42(void);
+extern void otg_trace_exit_w42(void);
+#endif /* defined(OTG_WINCE) */
+
+extern int otgtrace_init (void);
+extern void otgtrace_exit (void);
+extern int otg_trace_proc_read (char *page, int count, int * pos);
+extern int otg_trace_proc_write (const char *buf, int count, int * pos);
+
+/* usbp
+ */
+extern int usbd_device_init (void);
+extern void usbd_device_exit (void);
+
+
+/*
+ * otgcore/otg-mesg.c
+ */
+
+extern void otg_message(struct otg_instance *otg, char *buf);
+extern void otg_mesg_get_status_update(struct otg_status_update *status_update);
+extern void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info);
+extern int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info);
+extern struct otg_instance * mesg_otg_instance;
+
+
+/*
+ * otgcore/otg.c
+ */
+extern char * otg_get_state_names(int i);
+
+/*
+ * ops
+ */
+
+#define CORE core_trace_tag
+extern otg_tag_t CORE;
+
+extern struct hcd_instance * otg_set_hcd_ops(struct otg_instance *otg, struct hcd_ops *);
+extern struct ocd_instance * otg_set_ocd_ops(struct otg_instance *otg, struct ocd_ops *);
+extern struct pcd_instance * otg_set_pcd_ops(struct otg_instance *otg, struct pcd_ops *);
+extern struct tcd_instance * otg_set_tcd_ops(struct otg_instance *otg, struct tcd_ops *);
+extern int otg_set_usbd_ops(void *);
+
+extern void otg_get_ocd_info(struct otg_instance *otg, otg_tick_t *ticks, u16 *d_framenum);
+extern void otg_get_trace_info(struct otg_instance *otg, otg_trace_t *p);
+extern int otg_tmr_id_gnd(void);
+extern otg_tick_t otg_tmr_ticks(void);
+extern otg_tick_t otg_tmr_elapsed(otg_tick_t *t1, otg_tick_t *t2);
+extern u16 otg_tmr_framenum(void);
+extern u32 otg_tmr_interrupts(void);
+
+//extern void otg_write_info_message(struct usbd_bus_instance *, char *msg);
+
+
+/* @} */
diff --git a/drivers/otg/otg/otg-compat.h b/drivers/otg/otg/otg-compat.h
new file mode 100644
index 000000000000..bea2bbb09696
--- /dev/null
+++ b/drivers/otg/otg/otg-compat.h
@@ -0,0 +1,93 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-compat.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/linux/otg-compat.h|20070425221118|15928
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+/*!
+ * @file otg/otg/otg-compat.h
+ * @brief Common include file for Linux to determine and include appropriate OS compatibility file.
+ *
+ *
+ * @ingroup LINUXAPI
+ */
+#ifndef _OTG_COMPAT_H
+#define _OTG_COMPAT_H 1
+
+
+#include <otg/otg-utils.h>
+//#include <otg/otg-trace.h>
+
+
+#if defined(_WIN32_WCE)
+#define OTG_WINCE _WIN32_WCE
+#endif /* defined(_WIN32_WCE) */
+
+ /* What operating system are we running under? */
+
+ /* Recursively include enough information to determine which release */
+
+ #if (__GNUC__ >=3)
+ #define GCC3
+ #else
+ #define GCC2
+ #endif
+ #include <linux/kernel.h>
+ #include <linux/version.h>
+
+#if defined(__GNUC__)
+ #define OTG_LINUX
+ #ifndef CONFIG_OTG_NOC99
+ #define OTG_C99
+ #else
+ #endif
+
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
+ #define LINUX26
+ #elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5)
+ #define LINUX24
+ #else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */
+ #define LINUX24
+ #define LINUX_OLD
+ #warning "Early unsupported release of Linux kernel"
+ #endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */
+
+ /* We are running under a supported version of Linux */
+ #include <otg/otg-linux.h>
+
+
+#elif defined(OTG_WINCE)
+
+ /* We are running under a supported version of WinCE */
+ #include <otg/otg-wince.h>
+ #include <otg/otg-wince-ex.h>
+
+#else /* defined(OTG_WINCE) */
+
+ #error "Operating system not recognized"
+
+#endif /* defined(OTG_WINCE) */
+
+#if !defined(OTG_C99)
+#else /* !defined(OTG_C99) */
+#endif /* !defined(OTG_C99) */
+
+/* include otg-os.h - this will confirm that the otg-xxx.h file
+ * properly defined the os primitives.
+ */
+#include <otg/otg-os.h>
+#endif /* _OTG_COMPAT_H */
diff --git a/drivers/otg/otg/otg-dev.h b/drivers/otg/otg/otg-dev.h
new file mode 100644
index 000000000000..12ec8f3d41c7
--- /dev/null
+++ b/drivers/otg/otg/otg-dev.h
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-dev.h -- Generic PCI driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-dev.h|20070918212334|33755
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/otg-dev.h
+ * @brief Generic PCI Driver.
+ *
+ * This is the base PCI driver for supporting the PCI devices. It is
+ * setup to allow the various OTG drivers to register and be handled
+ * separately.
+ *
+ * Multiple devices are supported.
+ *
+ *
+ * TCD PCD HCD OCD
+ * | | | |
+ * -------------------------
+ * device PCI
+ * -------------------------
+ * otg PCI
+ * -------------------------
+ * OS PCI
+ * -------------------------
+ * hardware
+ *
+ *
+ * It will probe the hardware and support registration for the sub-device
+ * drivers (ocd, hcd and pcd).
+ *
+ * otg_dev_register_driver() and otg_dev_unregister_driver() are used
+ * to register the base PCI hardware driver information. Which should
+ * result in the hardware getting probed.
+ *
+ * otg_register_driver() and otg_unregister_driver() are used to register
+ * the ocd, pcd, hcd, tcd drivers.
+ *
+ * The Device PCI driver should ensure that the basic hardware and interrupt
+ * setup is performed during it's probe. Then call otg_dev_probe().
+ *
+ */
+
+#define OTG_DRIVER_TCD 0
+#define OTG_DRIVER_PCD 1
+#define OTG_DRIVER_HCD 2
+#define OTG_DRIVER_OCD 3
+#define OTG_DRIVER_TYPES 4
+
+
+struct otg_dev;
+
+struct otg_device_driver;
+
+/*! otg_dev_driver
+ * Functions used to start an otg sub driver.
+ */
+struct otg_dev_driver {
+ char *name;
+ int id;
+
+ u32 irqs;
+ irqreturn_t (*isr)(struct otg_dev *, void *, u32);
+
+ int (*probe)(struct otg_dev *);
+ void (*remove)(struct otg_dev *);
+
+#ifdef CONFIG_PM
+ void (*suspend)(struct otg_dev *dev, u32 state); /* Device suspended */
+ void (*resume)(struct otg_dev *dev); /* Device woken up */
+#endif /* CONFIG_PM */
+
+ void *ops;
+
+};
+
+struct otg_interrupt {
+ struct device *device;
+ struct platform_device *platform_device;
+ int irq;
+};
+
+/*! otg_device_driver
+ */
+struct otg_device_driver {
+
+ char *name;
+
+ /* device driver support */
+ //int (*device_otg_probe)(struct device *, struct otg_device_driver *);
+ //int (*device_otg_remove)(struct device *, struct otg_device_driver *);
+
+ int (*probe)(struct device *, struct otg_device_driver *);
+ int (*remove)(struct device *, struct otg_device_driver *);
+
+ /* platform device driver support */
+ int (*platform_otg_probe)(struct platform_device *, struct otg_device_driver *);
+ int (*platform_otg_remove)(struct platform_device *, struct otg_device_driver *);
+
+ /* interrupts */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+ irqreturn_t (*isr) (int irq, void *data, struct pt_regs *r);
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+ irqreturn_t (*isr) (int irq, void *data);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+
+ /* sub-drivers supporting various OTG functions from the hardware */
+ struct otg_dev_driver *drivers[OTG_DRIVER_TYPES];
+
+ char *serial_number;
+};
+
+
+struct otg_dev {
+ /* base hardware PCI driver and interrupt handler */
+ struct device *device;
+ struct platform_device *platform_device;
+
+ struct otg_device_driver *otg_device_driver;
+
+ struct otg_dev *next;
+
+ spinlock_t lock;
+
+ /* OTG Driver data */
+ void *drvdata;
+
+ /* interrupt support */
+ int num_resources;
+ struct otg_interrupt *otg_interrupts;
+
+ /* otg trace support */
+ otg_tag_t DEV;
+
+ char procfs[32];
+
+ struct otg_instance *otg_instance;
+
+ struct ocd_instance *ocd_instance;
+ struct pcd_instance *pcd_instance;
+ struct tcd_instance *tcd_instance;
+ struct hcd_instance *hcd_instance;
+
+ u32 interrupts;
+
+ void *privdata;
+};
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+extern irqreturn_t otg_dev_isr(int irq, void *data, struct pt_regs *r);
+extern irqreturn_t otg_pdev_isr(int irq, void *data, struct pt_regs *r);
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+extern irqreturn_t otg_dev_isr(int irq, void *data);
+extern irqreturn_t otg_pdev_isr(int irq, void *data);
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */
+
+extern int otg_dev_probe (struct device *, struct otg_device_driver *, void *privdata);
+extern void otg_dev_remove (struct device *, struct otg_device_driver *);
+
+extern int otg_pdev_probe (struct platform_device *, struct otg_device_driver *, void *privdata);
+extern void otg_pdev_remove (struct platform_device *, struct otg_device_driver *);
+
+extern int otg_dev_register_driver(struct otg_device_driver*, struct otg_dev_driver *);
+extern void otg_dev_unregister_driver(struct otg_device_driver*, struct otg_dev_driver *);
+
+extern void otg_dev_set_drvdata(struct otg_dev *, void *);
+extern void * otg_dev_get_drvdata(struct otg_dev *);
+
+
+
+/* End of FILE */
diff --git a/drivers/otg/otg/otg-fw-df.h b/drivers/otg/otg/otg-fw-df.h
new file mode 100644
index 000000000000..2bad1cfa0248
--- /dev/null
+++ b/drivers/otg/otg/otg-fw-df.h
@@ -0,0 +1,68 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-states-h.awk
+ *
+ * Do not Edit thie file
+ */
+
+/* %Z %K */
+
+/*!
+* @file otg/otg/otg-fw-df.h
+* @brief OTG Firmware - Firmware for df
+*
+* This file defines the OTG State Machine tests.
+*
+* @ingroup OTGFW
+*/
+
+
+/*!
+* @page OTGFW
+* @section OTGFW_SECTION - otg-fw-df.h
+* This contains the input, output and timout definitions for the OTG state machine firmware
+*/
+extern char otg_fw_name_df[];
+extern int otg_test_max_df;
+extern struct otg_test otg_tests_df[];
+
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ */
+ /*!
+ * This is the default Firmware. It is included in the
+ * compiled modules and supports the auto Traditional USB
+ * mode. No user inputs are implemented.
+ */
+
+#define invalid_state 0 /* State machine has been initialized. */
+
+#define otg_disabled 1 /* State machine has been initialized. */
+
+#define terminator_state 2 /* terminator */
+ /*
+ * This is not an OTG State. It is used internally to mark the end of the
+ * list of states and inputs.
+ */
+
+#define OTG_STATES_DF 3
+
+/* @} */
+
+extern struct otg_state otg_states_df[OTG_STATES_DF + 1];
+
+extern char *otg_get_state_name_df(int);
+
+extern struct otg_firmware otg_firmware_df;
diff --git a/drivers/otg/otg/otg-fw-mn.h b/drivers/otg/otg/otg-fw-mn.h
new file mode 100644
index 000000000000..a8140dd655e9
--- /dev/null
+++ b/drivers/otg/otg/otg-fw-mn.h
@@ -0,0 +1,352 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-states-h.awk
+ *
+ * Do not Edit thie file
+ */
+
+/* %Z %K */
+
+/*!
+* @file otg/otg/otg-fw-mn.h
+* @brief OTG Firmware - Firmware for mn
+*
+* This file defines the OTG State Machine tests.
+*
+* @ingroup OTGFW
+*/
+
+
+/*!
+* @page OTGFW
+* @section OTGFW_SECTION - otg-fw-mn.h
+* This contains the input, output and timout definitions for the OTG state machine firmware
+*/
+extern char otg_fw_name_mn[];
+extern int otg_test_max_mn;
+extern struct otg_test otg_tests_mn[];
+
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ */
+ /*!
+ * This is the initialization set for pcd, hcd and tcd.
+ */
+ /*!
+ * @name Meta State - otg_init
+ * @{
+ */
+
+#define invalid_state 0 /* Un-initialized state. */
+ /*
+ * This the initial state of the software when first loaded.
+ * It is not possible to return to this state.
+ */
+
+#define otg_disabled 1 /* State Machine ready but disabled. */
+ /*
+ * The USBOTG State Machine has been initialized but is inactive.
+ * This state may have arrived at from either the invalid_state or
+ * from the otg_disable state.
+ */
+
+#define otg_disable_tcd 2 /* State Machine waiting for driver de-initialization. */
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+
+#define otg_disable_hcd 3 /* State Machine waiting for driver de-initialization. */
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+
+#define otg_disable_pcd 4 /* State Machine waiting for driver de-initialization. */
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+
+#define otg_disable_ocd 5 /* State Machine waiting for driver de-initialization. */
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+
+#define otg_enable_ocd 6 /* State Machine waiting for driver initialization. */
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+
+#define otg_enable_pcd 7 /* State Machine waiting for driver initialization. */
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+
+#define otg_enable_hcd 8 /* State Machine waiting for driver initialization. */
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+
+#define otg_enable_tcd 9 /* State Machine waiting for driver initialization. */
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+ /* @} */
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ */
+ /*!
+ * This is the minimal firmware. It can be included in the
+ * compiled modules and supports the auto Traditional USB
+ * mode. No user inputs are required for normal operation.
+ *
+ * The b_bus_drop input can be optionally used to disconnect and re-connect.
+ *
+ * The enable_otg input can be optionally used to disable and re-enable.
+ * Note that disable/enable will reset b_bus_drop.
+ *
+ *
+ * B-Device Input Signals
+ *
+ * ID_GND/
+ * B_SESS_VLD
+ *
+ *
+ *
+ * A-Device Input Signals
+ *
+ * ID_GND
+ * DM_HIGH
+ * VBUS_VLD
+ * HUB_PORT_CONNECT
+ * BUS_RESET
+ * ADDRESSED
+ * CONFIGURED
+ * A_SESS_VLD/
+ *
+ *
+ *
+ */
+ /*!
+ * @name Meta State - otg_init
+ * @{
+ */
+
+#define otg_enabled 10 /* State machine has been initialized. */
+ /*!
+ * The State Machine has successfully started the device drivers and
+ * is waiting for an input event. Typically it will move from here to
+ * an idle state specific to the current conditions (peripheral_idle etc.)
+ * based on user request b_bus_drop.
+ *
+ */
+ /* @} */
+ /*!
+ * @name Meta State - b_idle
+ * @{
+ */
+
+#define peripheral_idle 11 /* Idle */
+ /*!
+ * USB Peripheral is idle.
+ * Waiting for Vbus to indicate that it has been plugged into a USB Host.
+ *
+ */
+
+#define peripheral_dropped 12 /* Dropped */
+ /*!
+ * USB Peripheral, user has dropped the bus.
+ */
+ /* @} */
+ /*!
+ * @name Meta State - b_peripheral
+ * @{
+ */
+
+#define peripheral_wait 13 /* Signal a connection and wait for packet traffic. */
+ /*!
+ * USB Peripheral, Vbus sensed, enabling pullup.
+ * The D+ pullup is enabled and we are waiting for a BUS_RESET to
+ * indicate that the USB Host has recognized that a USB Device is attached.
+ */
+
+#define peripheral_bus_reset 14 /* The bus has been reset. */
+ /*!
+ * USB Peripheral, waiting to be addressed.
+ * It is waiting to be enumerated and configured by the USB Host.
+ */
+
+#define peripheral_addressed 15 /* The device has been addressed. */
+ /*!
+ * The State Machine in the configured state for a Traditional USB Device.
+ * This means that there is an active session, there is packet traffic
+ * with this device.
+ */
+
+#define peripheral_configured 16 /* The device has been configured. */
+ /*!
+ * The State Machine in the configured state for a Traditional USB Device.
+ * This means that there is an active session, there is packet traffic
+ * with this device.
+ */
+
+#define peripheral_discharge_vbus 17 /* Discharging Vbus */
+ /*!
+ * The State Machine in the discharge state for a Traditional USB Device.
+ * The device has been unplugged. The Vbus discharge resistor will be enabled
+ * for the TLDISC_DSCHRG time period.
+ */
+ /* @} */
+ /*!
+ * @name Meta State - b_suspended
+ * @{
+ */
+
+#define peripheral_suspended 18 /* The bus has been suspended */
+ /*!
+ * The State Machine in the suspend state for a Traditional USB Device.
+ */
+
+#define peripheral_wakeup_enabled 19 /* Suspended with REMOTE WAKEUP enabled. */
+ /*!
+ * The State Machine in the suspend state for a Traditional USB Device,
+ * prior to suspended the USB Host enabled Remote Wakeup by sending a
+ * set REMOTE WAKUP request.
+ */
+
+#define peripheral_wakeup 20 /* Perform REMOTE WAKEUP procedure. */
+ /*!
+ * The State Machine in the wakeup state for a Traditional USB Device,
+ * The REMOTE WAKEUP procedure will be performed.
+ */
+ /* @} */
+
+#define host_idle 21 /* A-device idle. */
+ /*!
+ * A-Device idle state. An A-Plug is inserted in the Mini A-B Receptacle.
+ * This is the Host Only idle state. Waiting for user to allow
+ * the bus to be used.
+ *
+ * N.B. Reset all progress indicator inputs here.
+ */
+
+#define host_idle_dropped 22 /* A-device idle. */
+ /*!
+ * A-Device idle state. An A-Plug is inserted in the Mini A-B Receptacle.
+ * This is the Host Only idle state. Waiting for user to allow
+ * the bus to be used.
+ *
+ * N.B. Reset all progress indicator inputs here.
+ */
+
+#define host_wait_vrise 23 /* Check if charge pump should be enabled. */
+ /*!
+ * First check if external charge pump is required.
+ *
+ * XXX We force a wait of Ta_wait_vrise here, arriving in host wait
+ * port connect to quickly causes system hangs occasionally.
+ */
+
+#define host_wait_vrise_overcurrent 24 /* Overcurrent error. */
+ /*!
+ * A-Device Overcurrent condition (taking too long for Vbus to become valid.)
+ *
+ */
+
+#define host_wait_port_connect 25 /* Wait for a B-device connect. */
+ /*!
+ * Wait for a connection.
+ *
+ * A-Device wait for B-Device Connect. This is the normal route using the
+ * long debounce window.
+ *
+ * This state can optionally use the Ta_wait_bcon timeout (OTG mode) or
+ * wait forever (traditional USB Host mode.)
+ *
+ * XXX Arriving in this state to quickly from wait vrise can cause a system hang.
+ */
+
+#define host_port_connected 26 /* Reset the bus */
+ /*!
+ * A-Device host, the host controller driver has noticed a port status change,
+ * reset the bus and proceed.
+ */
+
+#define host_bus_reset 27 /* Address the device. */
+ /*!
+ * A-Device host, the bus has been reset, attempt to address the device.
+ */
+
+#define host_addressed 28 /* Enumerate the device. */
+ /*!
+ * A-Device host, the device has been addressed, attempt to enumerate,
+ * find the appropriate class driver and configure.
+ */
+
+#define host_configured 29 /* The device is configured. */
+ /*!
+ * A-Device host, the enumerated device is supported, and has been configured.
+ *
+ * XXX N.B. Currently assuming HNP enable feature is automatically performed.
+ * XXX N.B. Currently forcing HNP_CAPABLE and HNP_ENABLED.
+ */
+ /*!
+ * @name Meta State - a_vbus_err
+ * @{
+ */
+
+#define host_vbus_err 30 /* Vbus error. */
+ /*!
+ * A-Device Vbus error.
+ * The user must be informed and allowed to clear the problem.
+ */
+ /* @} */
+ /*!
+ * @name Meta State - host_wait_vfall
+ * @{
+ */
+
+#define host_wait_dischrg 31 /* Wait for Vbus to fall. */
+ /*!
+ */
+
+#define host_wait_vfall 32 /* Wait for Vbus to fall. */
+ /*!
+ * A-Device wait for Vbus to fall.
+ *
+ * XXX Currently reseting a_bus_req on entry, require explicit a_bus_req to proceed.
+ */
+
+#define terminator_state 33 /* terminator */
+ /*!
+ * This is not an OTG State. It is used internally to mark the end of the
+ * list of states and inputs.
+ */
+
+#define OTG_STATES_MN 34
+
+/* @} */
+
+extern struct otg_state otg_states_mn[OTG_STATES_MN + 1];
+
+extern char *otg_get_state_name_mn(int);
+
+extern struct otg_firmware otg_firmware_mn;
diff --git a/drivers/otg/otg/otg-fw.h b/drivers/otg/otg/otg-fw.h
new file mode 100644
index 000000000000..a04ccada9b18
--- /dev/null
+++ b/drivers/otg/otg/otg-fw.h
@@ -0,0 +1,1337 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-h.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+
+/*!
+* @defgroup OTGFW Firmware
+* @ingroup onthegogroup
+*/
+
+/*!
+* @file otg/otg/otg-fw.h
+* @brief OTG Firmware - Input, Output and Timeout definitions
+* This file defines the OTG State Machine input, output and timeout constants.
+*
+*
+* @ingroup OTGFW
+*/
+
+/*!
+* @name OTGFW
+* @section OTGFW_SECTION - otg-fw.h
+*/
+
+/*!
+* @name OTGVERSION OTG Version
+* Version information
+* @{
+*/
+
+#define OTG_VERSION_FW 200704251606L
+
+/*! @} */
+
+/*!
+* @name BASIC
+* These are the provided for application layer compatibility.
+* @{
+*/
+#ifdef OTG_APPLICATION
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned long u32;
+typedef unsigned long long u64;
+#include <stdio.h>
+#include <sys/ioctl.h>
+#endif /* OTG_APPLICATION */
+
+/*! @} */
+
+/*!
+* @name OTGSTRUCT OTG Structures
+* Basic OTG Structures
+*/
+#define OTGADMIN_MAXSTR 48
+
+/*!
+ * @struct otg_test
+ *
+ * This defines the tests that allow the state machine to move
+ * from state to state based on input events.
+ *
+ * Goto target: if (
+ * (!test1 || (test1 & inputs)) &&
+ * (!test2 || (test2 & inputs)) &&
+ *
+ */
+typedef struct otg_test {
+ u16 test; /*!< Test number */
+ u16 state; /*!< State Machine State */
+ u32 target; /*!< Goto this target if */
+ u64 test1; /*!< (!test1 || (test1 & inputs)) && */
+ u64 test2; /*!< (!test2 || (test2 & inputs)) && */
+ u64 test3; /*!< (!test3 || (test3 & inputs)) && */
+ /* N.B. An empty test is ignored. */
+} otg_test_t; /*!< */
+
+
+/*!
+ * @struct otg_input_name
+ * This structure simply allows for a table of input names that can
+ * searched for by input mask
+ */
+typedef struct otg_input_name {
+ u32 value; /*!< Bit value */
+ char name[OTGADMIN_MAXSTR]; /*!< Name of state. */
+} otg_input_name_t;
+
+
+/*!
+ * @struct otg_state
+ * This defines an OTG State. Each state has a name, meta state,
+ * timeout, reset and outputs
+ *
+ * On entry to the state the reset mask is or'd into the current
+ * input mask, the settings defined in the output mask are used to.
+ * call the required output functions and then if present the timer
+ * is started with the timeout value (in uSec).
+ *
+ */
+typedef struct otg_state {
+ u16 state; /*!< State Machine State */
+ u16 meta; /*!< Meta Machine State */
+ char name[OTGADMIN_MAXSTR]; /*!< Name of state. */
+ u32 tmout; /*!< Start timeout if non-zero */
+ u64 reset; /*!< Or these inputs on entry to state */
+ u64 outputs; /*!< Or these outputs on entry to state */
+} otg_state_t;
+
+
+/*!
+ * @struct otg_ioctl_name
+ * This structure allows a table for a table of IOCTL event names that can be
+ * searched for by the ioctl command value. It also stores the actual
+ * input mask to set or reset when according to the ioctl arguement.
+ */
+typedef struct otg_ioctl_name {
+ u32 cmd; /*!< Ioctl cmd */
+ u32 set; /*!< Signal to set/reset */
+ char *name; /*!< Name of ioctl. */
+} otg_ioctl_name_t;
+
+
+/*!
+ * @struct otg_admin_command
+ * This allows for a table of IOCTL admin commands.
+ */
+typedef struct otg_admin_command {
+ int n; /*!< Ioctl cmd */
+ char string[OTGADMIN_MAXSTR]; /*!< Signal to set/reset */
+} otg_admin_command_t;
+
+
+/*!
+ * @struct otg_status_update
+ * This is used by the OTG administrative programs to pass data to and received
+ * data from the OTG State Machine driver.
+ */
+typedef struct otg_status_update {
+ u16 state; /*!< current state */
+ u16 meta; /*!< current meta state */
+ u32 inputs; /*!< current inputs */
+ u64 outputs; /*!< current outputs */
+ u32 capabilities; /*!< */
+ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */
+ char state_name[OTGADMIN_MAXSTR]; /*!< name of current state */
+ char meta_name[OTGADMIN_MAXSTR]; /*!< name of current meta-state */
+ char function_name[OTGADMIN_MAXSTR]; /*!< currently selected function */
+} otg_status_update_t;
+
+
+/*!
+ * @struct otg_firmware_info
+ * This is used by the OTG Administrative program to pass firmware information
+ * to the OTG State Machine./
+ */
+typedef struct otg_firmware_info {
+ int number_of_states; /*!< number of states */
+ int number_of_tests; /*!< number of tests */
+ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */
+} otg_firmware_info_t;
+
+
+/*!
+ * @struct otg_firmware
+ * This is used by the OTG Administrative program to pass firmware
+ * to the OTG State Machine./
+ */
+typedef struct otg_firmware {
+ int number_of_states; /*!< number of states */
+ int number_of_tests; /*!< number of tests */
+ char fw_name[OTGADMIN_MAXSTR]; /*!< name of firmware */
+ struct otg_state *otg_states; /*!< output information */
+ struct otg_test *otg_tests; /*!< test information */
+} otg_firmware_t;
+
+/*! @} */
+
+/*!
+* @name OTGTIME Time Macros
+* Basic OTG Time Macros
+*/
+
+#define US(n) (n) /*!< micro-seconds */
+
+#define MS(n) (1000 * US(n)) /*!< milli-seconds */
+
+#define SEC(n) (1000 * MS(n)) /*!< seconds */
+
+/*! @} */
+
+/*!
+* @name OTGOUTPUTM Output Macros
+* OTG Output Macros
+*/
+
+/* OTG State Outputs
+ *
+ * Each state that we transition to defines new settings for each of the defined
+ * outputs. Each output setting can have four settings:
+ *
+ *
+ *
+ *
+ *
+ */
+#define NC ((u64)0x0) /*!< NC no change */
+#define SET ((u64)0x1) /*!< SET the output should be set (i.e. enabled) */
+#define RESET ((u64)0x2) /*!< RESET the output should be reset (i.e. disabled) */
+#define PULSE ((u64)0x3) /*!< OTHER special setting, for example used for pulse vbus */
+#define POWER ((u64)0x3) /*!< POWER */
+
+#define _MASK(n) (((u64) 1) << n) /*!< Set nth bit in 64 bit mask */
+#define _NOT(m) (((u64) m) << 32) /*!< Shift left mask 32 bits */
+#define _ncmask(n) (NC) /*!< No output change mask */
+#define _setmask(n) (SET << (n*2)) /*!< Output set mask */
+#define _resetmask(n) (RESET << (n*2)) /*!< Output reset mask */
+#define _pulsemask(n) (PULSE << (n*2)) /*!< Output pulse mask */
+#define _powermask(n) (POWER << (n*2)) /*!< Output poser mask */
+
+/*! @} */
+
+/*!
+* @name OTGNAMES OTG Names
+* Basic OTG Name Tables
+*/
+
+extern struct otg_input_name otg_input_names[];
+extern struct otg_ioctl_name otg_ioctl_names[];
+extern struct otg_firmware *otg_firmware_loaded;
+extern char *otg_output_names[];
+
+
+/*! @} */
+
+/* Generated by otg-inputs-h.awk
+ *
+ * Do not Edit this file.
+ */
+
+/* %Z %K */
+
+
+ /*!
+ * N.B. The OTG State Machine Documentation uses the syntax A and A/
+ * to indicate an event being true or not true. Because a trailing
+ * slash is not legal in C we use A and A_ to indicate true and not true.
+ */
+
+ /*!
+ * @name OTG Transceiver Inputs
+ * These inputs reflect changes seen in the OTG Transceiver. These
+ * what are available in most common OTG transceivers, for example
+ * the ISP1301 or MAX3353.
+ *
+ * The following are typical Vbus comparators.
+ *
+ * ......................................ISP1301.........MAX3353E
+ * B-Session.end.threshold...............0.2V-0.8V.......0.5V....
+ * Session.Valid.Comparator..............0.8V-2.0V.......1.4V....
+ * B-Session.Valid.......................2.0V-4.0V.......N/A.....
+ * A-Device.Vbus.Valid.Comparator........4.4V............4.6V....
+ *
+ * @{
+ */
+#define ID_GND _MASK( 0) /*!< otg_ok - ID is grounded */
+#define ID_GND_ _NOT(ID_GND)
+#define ID_FLOAT _MASK( 1) /*!< otg_ok - ID is floating */
+#define ID_FLOAT_ _NOT(ID_FLOAT)
+#define DP_HIGH _MASK( 2) /*!< otg_ok - DP is pulled high */
+#define DP_HIGH_ _NOT(DP_HIGH)
+#define DM_HIGH _MASK( 3) /*!< otg_ok - DM pullup is pulled high */
+#define DM_HIGH_ _NOT(DM_HIGH)
+#define B_SESS_END _MASK( 4) /*!< otg_ok - Vbus less than B-Session end thresshold */
+#define B_SESS_END_ _NOT(B_SESS_END)
+#define A_SESS_VLD _MASK( 5) /*!< otg_ok - Vbus greater than Session valid threshold */
+#define A_SESS_VLD_ _NOT(A_SESS_VLD)
+#define B_SESS_VLD _MASK( 6) /*!< otg_ok - Vbus greater than B-Session Valid threshold */
+#define B_SESS_VLD_ _NOT(B_SESS_VLD)
+#define VBUS_VLD _MASK( 7) /*!< otg_ok - Vbus greater than A-Device Vbus Valid threshold */
+#define VBUS_VLD_ _NOT(VBUS_VLD)
+#define SRP_DET _MASK( 8) /*!< a_idle - SRP Detected (Vbus pulsed) */
+#define SRP_DET_ _NOT(SRP_DET)
+#define SE0_DET _MASK( 9) /*!< b_idle - SE0 Detected (Single Ended Zeros, DM and DP both low) */
+#define SE0_DET_ _NOT(SE0_DET)
+#define SE1_DET _MASK(10) /*!< b_idle - SE1 Detected (Single Ended Ones, DM and DP both high) */
+#define SE1_DET_ _NOT(SE1_DET)
+#define BDIS_ACON _MASK(10) /*!< a_hnp_wait - Auto DP pullup high after B-disconnect */
+#define BDIS_ACON_ _NOT(BDIS_ACON)
+#define CR_INT_DET _MASK(10) /*!< ph_audio - 0.4V < DP < 0.6V */
+#define CR_INT_DET_ _NOT(CR_INT_DET)
+ /* @} */
+ /*!
+ * @name Peripheral and Host driver signals.
+ *
+ *
+ * @{
+ */
+#define HUB_PORT_CONNECT _MASK(11) /*!< otg_both - Port Connection */
+#define HUB_PORT_CONNECT_ _NOT(HUB_PORT_CONNECT)
+#define BUS_RESET _MASK(12) /*!< otg_both - Bus has reset. */
+#define BUS_RESET_ _NOT(BUS_RESET)
+#define ADDRESSED _MASK(13) /*!< otg_both - Device has been addressed (received SET ADDRESS request.) */
+#define ADDRESSED_ _NOT(ADDRESSED)
+#define DEVICE_REQUEST _MASK(14) /*!< otg_both - Device has received device request (received SETUP request.) */
+#define DEVICE_REQUEST_ _NOT(DEVICE_REQUEST)
+#define CONFIGURED _MASK(15) /*!< otg_both - Device has been configured (received SET CONFIG request.) */
+#define CONFIGURED_ _NOT(CONFIGURED)
+#define NOT_SUPPORTED _MASK(16) /*!< otg_both - Peripheral not supported (no class driver.) */
+#define NOT_SUPPORTED_ _NOT(NOT_SUPPORTED)
+#define BUS_SUSPENDED _MASK(17) /*!< otg_both - Bus has been suspended. */
+#define BUS_SUSPENDED_ _NOT(BUS_SUSPENDED)
+ /* @} */
+
+ /*!
+ * @name Administrative Policy Inputs
+ * These are only valid in the state indicated and
+ * can be enabled or disabled.
+ *
+ * @{
+ */
+#define a_bcon_no_tmout_req _MASK(18) /*!< otg_host - Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode). */
+#define a_bcon_no_tmout_req_ _NOT(a_bcon_no_tmout_req)
+#define a_hpwr_req _MASK(19) /*!< otg_host - Application on A-host wants external charge pump enabled. */
+#define a_hpwr_req_ _NOT(a_hpwr_req)
+#define bus_drop _MASK(20) /*!< otg_ok - Application on Device needs to power down bus. */
+#define bus_drop_ _NOT(bus_drop)
+#define a_bus_drop _MASK(20) /*!< otg_ok - Application on A-Device needs to power down bus. */
+#define a_bus_drop_ _NOT(a_bus_drop)
+#define b_bus_drop _MASK(20) /*!< otg_ok - Application on B-Device needs to disconnect from bus. */
+#define b_bus_drop_ _NOT(b_bus_drop)
+#define bus_req _MASK(21) /*!< otg_ok - Application on Device wants to use the bus. */
+#define bus_req_ _NOT(bus_req)
+#define a_bus_req _MASK(21) /*!< otg_ok - Application on A-Device wants to act as host */
+#define a_bus_req_ _NOT(a_bus_req)
+#define b_bus_req _MASK(21) /*!< otg_ok - Application on B-Device wants to act as host */
+#define b_bus_req_ _NOT(b_bus_req)
+#define b_sess_req _MASK(21) /*!< otg_ok - Application on B-Device to perform SRP (alias for b_srp_req.) */
+#define b_sess_req_ _NOT(b_sess_req)
+#define suspend_req _MASK(22) /*!< otg_host - Application on Device requests bus be suspended (alias for a_bus_req/.) */
+#define suspend_req_ _NOT(suspend_req)
+#define a_suspend_req _MASK(22) /*!< otg_host - Application on A-host requests bus be suspended (alias for a_bus_req/.) */
+#define a_suspend_req_ _NOT(a_suspend_req)
+#define b_suspend_req _MASK(22) /*!< otg_host - Application on B-host requests bus be suspended (alias for b_bus_req/.) */
+#define b_suspend_req_ _NOT(b_suspend_req)
+ /* @} */
+
+ /*!
+ * @name Administrative Action Inputs
+ * These are only valid in state indicated. The
+ * state machine tests must reset in states
+ * prior and after use.
+ * @{
+ */
+#define set_remote_wakeup_cmd _MASK(24) /*!< a_host - A-Device will send Remote Wakeup Enable Request */
+#define set_remote_wakeup_cmd_ _NOT(set_remote_wakeup_cmd)
+#define remote_wakeup_cmd _MASK(24) /*!< tr_configured - B-Device will perform Remote Wakeup. */
+#define remote_wakeup_cmd_ _NOT(remote_wakeup_cmd)
+#define reset_remote_wakeup_cmd _MASK(25) /*!< a_host - A-Device will send Remote Wakeup Disable Request */
+#define reset_remote_wakeup_cmd_ _NOT(reset_remote_wakeup_cmd)
+#define clr_err_cmd _MASK(25) /*!< a_vbus_err - A-Device ill clears Vbus overcurrent error. */
+#define clr_err_cmd_ _NOT(clr_err_cmd)
+#define b_hnp_cmd _MASK(25) /*!< b_configured - B-Device will attempt HNP */
+#define b_hnp_cmd_ _NOT(b_hnp_cmd)
+#define ph_int_cmd _MASK(25) /*!< ph_audio - B-Device will request Carkit interrupt */
+#define ph_int_cmd_ _NOT(ph_int_cmd)
+#define ph_audio_cmd _MASK(25) /*!< ph_uart - Application on B-Device connected to Carkit requests audio mode. */
+#define ph_audio_cmd_ _NOT(ph_audio_cmd)
+#define cr_int_cmd _MASK(25) /*!< cr_aud - Application on A-Device wants to emulate Carkit */
+#define cr_int_cmd_ _NOT(cr_int_cmd)
+#define led_on_cmd _MASK(26) /*!< ph_uart - B-Device will enable Carkit LED */
+#define led_on_cmd_ _NOT(led_on_cmd)
+#define led_off_cmd _MASK(27) /*!< ph_uart - B-Device will disable Carkit LED */
+#define led_off_cmd_ _NOT(led_off_cmd)
+ /* @} */
+
+ /*!
+ * @name Internal State
+ * Used to track status changes.
+ * @{
+ */
+#define HNP_ENABLED _MASK(27) /*!< b_configured - B-HNP Enable Request sent (a-host) or received (b-peripheral). */
+#define HNP_ENABLED_ _NOT(HNP_ENABLED)
+#define HNP_CAPABLE _MASK(28) /*!< otg_both - B-host Peripheral may do HNP */
+#define HNP_CAPABLE_ _NOT(HNP_CAPABLE)
+#define HNP_SUPPORTED _MASK(28) /*!< otg_both - B-host can do HNP (A-Host Received HNP Supported SET FEATURE) */
+#define HNP_SUPPORTED_ _NOT(HNP_SUPPORTED)
+#define REMOTE_WAKEUP_ENABLED _MASK(29) /*!< otg_configured - Remote Wakeup Enable Request received. */
+#define REMOTE_WAKEUP_ENABLED_ _NOT(REMOTE_WAKEUP_ENABLED)
+#define REMOTE_CAPABLE _MASK(29) /*!< otg_both - Peripheral can do remote wakeup */
+#define REMOTE_CAPABLE_ _NOT(REMOTE_CAPABLE)
+ /* @} */
+
+ /*!
+ * @name Global Administration
+ * @{
+ */
+ /* @} */
+
+ /*!
+ * @name Driver Initialization Finished signals
+ * @{
+ */
+#define PCD_OK _MASK(30) /*!< otg_driver - PCD Driver Initialization Finished. */
+#define PCD_OK_ _NOT(PCD_OK)
+#define TCD_OK _MASK(30) /*!< otg_driver - TCD Driver Initialization Finished. */
+#define TCD_OK_ _NOT(TCD_OK)
+#define HCD_OK _MASK(30) /*!< otg_driver - HCD Driver Initialization Finished. */
+#define HCD_OK_ _NOT(HCD_OK)
+#define OCD_OK _MASK(30) /*!< otg_driver - OCD Driver Initialization Finished. */
+#define OCD_OK_ _NOT(OCD_OK)
+ /* @} */
+
+ /*!
+ * @name Timeout and enable
+ * @{
+ */
+#define TMOUT _MASK(30) /*!< otg_all - Generic Timeout */
+#define TMOUT_ _NOT(TMOUT)
+#define enable_otg _MASK(31) /*!< otg_all - Move State Machine otg_disabled state. */
+#define enable_otg_ _NOT(enable_otg)
+#define AUTO _MASK(31) /*!< otg_all - Auto Return (enable_otg only true when active) */
+#define AUTO_ _NOT(AUTO)
+ /* @} */
+
+ /*! @name Timeouts C.f. OTG Table 5-2 A-Device Timing
+ * @{
+ */
+#define Ta_wait_vrise _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */
+#define Ta_wait_vrise_ _NOT(Ta_wait_vrise)
+#define TA_WAIT_VRISE MS(100)
+#define Ta_wait_vrise_200 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */
+#define Ta_wait_vrise_200_ _NOT(Ta_wait_vrise_200)
+#define TA_WAIT_VRISE_200 MS(200)
+#define Ta_wait_vrise_400 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */
+#define Ta_wait_vrise_400_ _NOT(Ta_wait_vrise_400)
+#define TA_WAIT_VRISE_400 MS(400)
+#define Ta_wait_vrise_500 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */
+#define Ta_wait_vrise_500_ _NOT(Ta_wait_vrise_500)
+#define TA_WAIT_VRISE_500 MS(500)
+#define Ta_wait_vrise_800 _MASK(30) /*!< a_wait_vrise - Wait for Vbus Rise */
+#define Ta_wait_vrise_800_ _NOT(Ta_wait_vrise_800)
+#define TA_WAIT_VRISE_800 MS(800)
+#define Ta_bcon_ldb _MASK(30) /*!< a_bcon_ldb - B-Connect Long Debounce */
+#define Ta_bcon_ldb_ _NOT(Ta_bcon_ldb)
+#define TA_BCON_LDB MS(100)
+#define Ta_wait_bcon _MASK(30) /*!< a_wait_bcon - Wait for 1 second for B-Connect */
+#define Ta_wait_bcon_ _NOT(Ta_wait_bcon)
+#define TA_WAIT_BCON SEC(1)
+#define Ta_wait_bcon_5 _MASK(30) /*!< a_wait_bcon - Wait for 5 second for B-Connect */
+#define Ta_wait_bcon_5_ _NOT(Ta_wait_bcon_5)
+#define TA_WAIT_BCON_5 SEC(5)
+#define Ta_wait_bcon_10 _MASK(30) /*!< a_wait_bcon - Wait for 10 second for B-Connect */
+#define Ta_wait_bcon_10_ _NOT(Ta_wait_bcon_10)
+#define TA_WAIT_BCON_10 SEC(10)
+#define Ta_aidl_bdis _MASK(30) /*!< a_hnp_wait - A-Idle to B-Disconnect */
+#define Ta_aidl_bdis_ _NOT(Ta_aidl_bdis)
+#define TA_AIDL_BDIS MS(200)
+#define Ta_bdis_acon _MASK(30) /*!< a_suspend - B-disconnect to A-Connect */
+#define Ta_bdis_acon_ _NOT(Ta_bdis_acon)
+#define TA_BDIS_ACON MS(3)
+#define Ta_bidl_adis_min _MASK(30) /*!< a_peripheral - B-Idle to A-Disconnect minimum (TODO) */
+#define Ta_bidl_adis_min_ _NOT(Ta_bidl_adis_min)
+#define TA_BIDL_ADIS_MIN MS(3)
+#define Ta_bcon_sdb _MASK(30) /*!< a_bcon_sdb - B-Connect Short Debounce */
+#define Ta_bcon_sdb_ _NOT(Ta_bcon_sdb)
+#define TA_BCON_SDB US(2)
+#define Ta_bcon_sdb_win _MASK(30) /*!< a_bcon_win - B-Connect Short Debounce Window */
+#define Ta_bcon_sdb_win_ _NOT(Ta_bcon_sdb_win)
+#define TA_BCON_SDB_WIN MS(100)
+ /* @} */
+
+ /*! @name Timeouts C.f. OTG Table 5-3 B-Device Timing
+ * @{
+ */
+#define Tb_se0_srp _MASK(30) /*!< b_srp_se0 - SE0 Time Before SRP */
+#define Tb_se0_srp_ _NOT(Tb_se0_srp)
+#define TB_SE0_SRP MS(2)
+#define Tb_data_pls _MASK(30) /*!< b_srp_init - Data-Line Pulse Time */
+#define Tb_data_pls_ _NOT(Tb_data_pls)
+#define TB_DATA_PLS US(5500)
+#define Tb_data_pls_min _MASK(30) /*!< a_srp_min - Data-Line Pulse minimum time (5mS-3mSidle) */
+#define Tb_data_pls_min_ _NOT(Tb_data_pls_min)
+#define TB_DATA_PLS_MIN MS(4)
+#define Tb_data_pls_max _MASK(30) /*!< a_srp_wait - Data-Line Pulse maximum time (10mS-3mS idle) */
+#define Tb_data_pls_max_ _NOT(Tb_data_pls_max)
+#define TB_DATA_PLS_MAX MS(7)
+#define Tb_srp_init _MASK(30) /*!< b_srp_init - SRP Initiate Time (TODO multi-state? Not-needed?) */
+#define Tb_srp_init_ _NOT(Tb_srp_init)
+#define TB_SRP_INIT MS(100)
+#define Tb_srp_fail_min _MASK(30) /*!< b_srp_wait - SRP Fail Time minimum (TODO) */
+#define Tb_srp_fail_min_ _NOT(Tb_srp_fail_min)
+#define TB_SRP_FAIL_MIN SEC(5)
+#define Tb_aidl_bdis_min _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect minimum (TODO) */
+#define Tb_aidl_bdis_min_ _NOT(Tb_aidl_bdis_min)
+#define TB_AIDL_BDIS_MIN MS(5)
+#define Tb_aidl_bdis_max _MASK(30) /*!< b_peripheral - A-idle to B-Disconnect maximum (TODO) */
+#define Tb_aidl_bdis_max_ _NOT(Tb_aidl_bdis_max)
+#define TB_AIDL_BDIS_MAX MS(150)
+#define Tldisc_dschrg _MASK(30) /*!< b_dischrg - Local Disconnect to Data Line Discharge (TODO) */
+#define Tldisc_dschrg_ _NOT(Tldisc_dschrg)
+#define TLDISC_DSCHRG US(25)
+#define Tb_ase0_brst_min _MASK(30) /*!< b_wait_acon - A-SE0 to B-Reset minimum */
+#define Tb_ase0_brst_min_ _NOT(Tb_ase0_brst_min)
+#define TB_ASE0_BRST_MIN US(3125)
+#define Tb_acon_dbnc _MASK(30) /*!< b_acon_dbnc - A-Connect Debounce */
+#define Tb_acon_dbnc_ _NOT(Tb_acon_dbnc)
+#define TB_ACON_DBNC US(2)
+#define Tb_acon_bse0 _MASK(30) /*!< b_host_se0 - A-Connect to B-SE0 */
+#define Tb_acon_bse0_ _NOT(Tb_acon_bse0)
+#define TB_ACON_BSE0 MS(1)
+#define Tid_ldb _MASK(30) /*!< otg_enable - ID changes debounce */
+#define Tid_ldb_ _NOT(Tid_ldb)
+#define TID_LDB MS(100)
+ /* @} */
+
+ /*! @name Timeouts for Carkit
+ * @{
+ */
+#define Tph_bcon_ldb _MASK(30) /*!< ph_init - B-Connect Long Debounce */
+#define Tph_bcon_ldb_ _NOT(Tph_bcon_ldb)
+#define TPH_BCON_LDB MS(100)
+#define Tph_init_pls _MASK(30) /*!< ph_int - Timeout for Carkit interrupt. */
+#define Tph_init_pls_ _NOT(Tph_init_pls)
+#define TPH_INIT_PLS US(500)
+#define Tcr_uart_rsp _MASK(30) /*!< ph_int - Timeout for Carkit UART */
+#define Tcr_uart_rsp_ _NOT(Tcr_uart_rsp)
+#define TCR_UART_RSP MS(30)
+#define Tcr_aud_det _MASK(30) /*!< ph_audio_wait - Timeout waiting for Carkit Audio ack */
+#define Tcr_aud_det_ _NOT(Tcr_aud_det)
+#define TCR_AUD_DET US(1000)
+#define Tph_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */
+#define Tph_led_off_ _NOT(Tph_led_off)
+#define TPH_LED_OFF US(10)
+#define Tph_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */
+#define Tph_led_on_ _NOT(Tph_led_on)
+#define TPH_LED_ON MS(4)
+#define Tcr_led_off _MASK(30) /*!< ph_uart - Timeout for LED off pulse */
+#define Tcr_led_off_ _NOT(Tcr_led_off)
+#define TCR_LED_OFF MS(4)
+#define Tcr_led_on _MASK(30) /*!< ph_uart - Timeout for LED on pulse */
+#define Tcr_led_on_ _NOT(Tcr_led_on)
+#define TCR_LED_ON MS(8)
+#define Tcr_dm_disc _MASK(30) /*!< cr_aud - Timeout for DM disconnect */
+#define Tcr_dm_disc_ _NOT(Tcr_dm_disc)
+#define TCR_DM_DISC MS(50)
+#define Tph_mono_ack _MASK(30) /*!< cr_wait - Timeout for CR INT */
+#define Tph_mono_ack_ _NOT(Tph_mono_ack)
+#define TPH_MONO_ACK MS(1)
+#define Tph_aud_det _MASK(30) /*!< cr_ack - Timeout for CR ACK */
+#define Tph_aud_det_ _NOT(Tph_aud_det)
+#define TPH_AUD_DET MS(100)
+ /* @} */
+
+ /*! @name Timeouts for timer test
+ * @{
+ */
+#define Tzero _MASK(30) /*!< otg_enabled - Startup */
+#define Tzero_ _NOT(Tzero)
+#define TZERO MS(2)
+#define Tst_100_us _MASK(30) /*!< tm_start - Timer test */
+#define Tst_100_us_ _NOT(Tst_100_us)
+#define TST_100_US US(100)
+#define Tst_one_ms _MASK(30) /*!< tm_start - Timer test */
+#define Tst_one_ms_ _NOT(Tst_one_ms)
+#define TST_ONE_MS MS(1)
+#define Tst_ten_ms _MASK(30) /*!< tm_start - Timer test */
+#define Tst_ten_ms_ _NOT(Tst_ten_ms)
+#define TST_TEN_MS MS(10)
+#define Tst_one_second _MASK(30) /*!< tm_start - Timer test */
+#define Tst_one_second_ _NOT(Tst_one_second)
+#define TST_ONE_SECOND SEC(1)
+#define Tst_two_second _MASK(30) /*!< tm_start - Timer test */
+#define Tst_two_second_ _NOT(Tst_two_second)
+#define TST_TWO_SECOND SEC(2)
+#define Tst_four_second _MASK(30) /*!< tm_start - Timer test */
+#define Tst_four_second_ _NOT(Tst_four_second)
+#define TST_FOUR_SECOND SEC(4)
+#define Tst_eight_second _MASK(30) /*!< tm_start - Timer test */
+#define Tst_eight_second_ _NOT(Tst_eight_second)
+#define TST_EIGHT_SECOND SEC(8)
+#define Tst_ten_second _MASK(30) /*!< tm_start - Timer test */
+#define Tst_ten_second_ _NOT(Tst_ten_second)
+#define TST_TEN_SECOND SEC(10)
+ /* @} */
+
+/* Generated by otg-outputs-h.awk
+ *
+ * Do not Edit this file.
+ */
+
+/* %Z %K */
+
+
+
+ /* State Machine Outputs
+ */
+
+ /*! @name Driver Initialization Outputs
+ * N.B. tcd_en is used for older devices, to check if Vbus
+ * already enabled.
+ * @{
+ */
+
+#define TCD_INIT_OUT 0 /*!< Initiate Transceiver Controller Driver Initialization (or De-initialization.) */
+#define tcd_init_out _setmask(TCD_INIT_OUT)
+#define tcd_init_out_ _resetmask(TCD_INIT_OUT)
+#define tcd_init_out_set _setmask(TCD_INIT_OUT)
+#define tcd_init_out_reset _resetmask(TCD_INIT_OUT)
+#define tcd_init_out_power _powermask(TCD_INIT_OUT)
+
+#define PCD_INIT_OUT 1 /*!< Initiate Peripheral Controller Driver Initialization (or De-initialization.) */
+#define pcd_init_out _setmask(PCD_INIT_OUT)
+#define pcd_init_out_ _resetmask(PCD_INIT_OUT)
+#define pcd_init_out_set _setmask(PCD_INIT_OUT)
+#define pcd_init_out_reset _resetmask(PCD_INIT_OUT)
+#define pcd_init_out_power _powermask(PCD_INIT_OUT)
+
+#define HCD_INIT_OUT 2 /*!< Initiate Host Controller Driver Initialization (or De-initialization). */
+#define hcd_init_out _setmask(HCD_INIT_OUT)
+#define hcd_init_out_ _resetmask(HCD_INIT_OUT)
+#define hcd_init_out_set _setmask(HCD_INIT_OUT)
+#define hcd_init_out_reset _resetmask(HCD_INIT_OUT)
+#define hcd_init_out_power _powermask(HCD_INIT_OUT)
+
+#define OCD_INIT_OUT 3 /*!< Initiate OTG Controller Driver Initialization (or De-initialization). */
+#define ocd_init_out _setmask(OCD_INIT_OUT)
+#define ocd_init_out_ _resetmask(OCD_INIT_OUT)
+#define ocd_init_out_set _setmask(OCD_INIT_OUT)
+#define ocd_init_out_reset _resetmask(OCD_INIT_OUT)
+#define ocd_init_out_power _powermask(OCD_INIT_OUT)
+
+#define TCD_EN_OUT 4 /*!< Enable Transceiver Controller Driver */
+#define tcd_en_out _setmask(TCD_EN_OUT)
+#define tcd_en_out_ _resetmask(TCD_EN_OUT)
+#define tcd_en_out_set _setmask(TCD_EN_OUT)
+#define tcd_en_out_reset _resetmask(TCD_EN_OUT)
+#define tcd_en_out_power _powermask(TCD_EN_OUT)
+
+#define PCD_EN_OUT 5 /*!< Enable Peripheral Controller Driver */
+#define pcd_en_out _setmask(PCD_EN_OUT)
+#define pcd_en_out_ _resetmask(PCD_EN_OUT)
+#define pcd_en_out_set _setmask(PCD_EN_OUT)
+#define pcd_en_out_reset _resetmask(PCD_EN_OUT)
+#define pcd_en_out_power _powermask(PCD_EN_OUT)
+
+#define HCD_EN_OUT 6 /*!< Enable Host Controller Driver */
+#define hcd_en_out _setmask(HCD_EN_OUT)
+#define hcd_en_out_ _resetmask(HCD_EN_OUT)
+#define hcd_en_out_set _setmask(HCD_EN_OUT)
+#define hcd_en_out_reset _resetmask(HCD_EN_OUT)
+#define hcd_en_out_power _powermask(HCD_EN_OUT)
+ /* @) */
+
+ /*! @name Transceiver Controller Driver Outputs
+ * @{
+ */
+
+#define DRV_VBUS_OUT 7 /*!< A-Device will Drive Vbus to 5V through charge pump. */
+#define drv_vbus_out _setmask(DRV_VBUS_OUT)
+#define drv_vbus_out_ _resetmask(DRV_VBUS_OUT)
+#define drv_vbus_out_set _setmask(DRV_VBUS_OUT)
+#define drv_vbus_out_reset _resetmask(DRV_VBUS_OUT)
+#define drv_vbus_out_power _powermask(DRV_VBUS_OUT)
+
+#define CHRG_VBUS_OUT 8 /*!< B-Device will charge Vbus to 3.3V through resistor (SRP.) */
+#define chrg_vbus_out _setmask(CHRG_VBUS_OUT)
+#define chrg_vbus_out_ _resetmask(CHRG_VBUS_OUT)
+#define chrg_vbus_out_set _setmask(CHRG_VBUS_OUT)
+#define chrg_vbus_out_reset _resetmask(CHRG_VBUS_OUT)
+#define chrg_vbus_out_power _powermask(CHRG_VBUS_OUT)
+
+#define DISCHRG_VBUS_OUT 9 /*!< B-Device will discharge Vbus (enable dischage resistor.) */
+#define dischrg_vbus_out _setmask(DISCHRG_VBUS_OUT)
+#define dischrg_vbus_out_ _resetmask(DISCHRG_VBUS_OUT)
+#define dischrg_vbus_out_set _setmask(DISCHRG_VBUS_OUT)
+#define dischrg_vbus_out_reset _resetmask(DISCHRG_VBUS_OUT)
+#define dischrg_vbus_out_power _powermask(DISCHRG_VBUS_OUT)
+
+#define DM_PULLUP_OUT 10 /*!< DM pullup control - aka loc_carkit */
+#define dm_pullup_out _setmask(DM_PULLUP_OUT)
+#define dm_pullup_out_ _resetmask(DM_PULLUP_OUT)
+#define dm_pullup_out_set _setmask(DM_PULLUP_OUT)
+#define dm_pullup_out_reset _resetmask(DM_PULLUP_OUT)
+#define dm_pullup_out_power _powermask(DM_PULLUP_OUT)
+
+#define DM_PULLDOWN_OUT 11 /*!< DM pulldown control */
+#define dm_pulldown_out _setmask(DM_PULLDOWN_OUT)
+#define dm_pulldown_out_ _resetmask(DM_PULLDOWN_OUT)
+#define dm_pulldown_out_set _setmask(DM_PULLDOWN_OUT)
+#define dm_pulldown_out_reset _resetmask(DM_PULLDOWN_OUT)
+#define dm_pulldown_out_power _powermask(DM_PULLDOWN_OUT)
+
+#define DP_PULLUP_OUT 12 /*!< DP pullup control - aka loc_conn */
+#define dp_pullup_out _setmask(DP_PULLUP_OUT)
+#define dp_pullup_out_ _resetmask(DP_PULLUP_OUT)
+#define dp_pullup_out_set _setmask(DP_PULLUP_OUT)
+#define dp_pullup_out_reset _resetmask(DP_PULLUP_OUT)
+#define dp_pullup_out_power _powermask(DP_PULLUP_OUT)
+
+#define DP_PULLDOWN_OUT 13 /*!< DP pulldown control */
+#define dp_pulldown_out _setmask(DP_PULLDOWN_OUT)
+#define dp_pulldown_out_ _resetmask(DP_PULLDOWN_OUT)
+#define dp_pulldown_out_set _setmask(DP_PULLDOWN_OUT)
+#define dp_pulldown_out_reset _resetmask(DP_PULLDOWN_OUT)
+#define dp_pulldown_out_power _powermask(DP_PULLDOWN_OUT)
+
+#define CLR_OVERCURRENT_OUT 14 /*!< Clear overcurrent indication */
+#define clr_overcurrent_out _setmask(CLR_OVERCURRENT_OUT)
+#define clr_overcurrent_out_ _resetmask(CLR_OVERCURRENT_OUT)
+#define clr_overcurrent_out_set _setmask(CLR_OVERCURRENT_OUT)
+#define clr_overcurrent_out_reset _resetmask(CLR_OVERCURRENT_OUT)
+#define clr_overcurrent_out_power _powermask(CLR_OVERCURRENT_OUT)
+
+#define DM_DET_OUT 15 /*!< Enable B-Device D- High detect */
+#define dm_det_out _setmask(DM_DET_OUT)
+#define dm_det_out_ _resetmask(DM_DET_OUT)
+#define dm_det_out_set _setmask(DM_DET_OUT)
+#define dm_det_out_reset _resetmask(DM_DET_OUT)
+#define dm_det_out_power _powermask(DM_DET_OUT)
+
+#define DP_DET_OUT 16 /*!< Enable B-Device D+ High detect */
+#define dp_det_out _setmask(DP_DET_OUT)
+#define dp_det_out_ _resetmask(DP_DET_OUT)
+#define dp_det_out_set _setmask(DP_DET_OUT)
+#define dp_det_out_reset _resetmask(DP_DET_OUT)
+#define dp_det_out_power _powermask(DP_DET_OUT)
+
+#define CR_DET_OUT 17 /*!< Enable D+ CR detect */
+#define cr_det_out _setmask(CR_DET_OUT)
+#define cr_det_out_ _resetmask(CR_DET_OUT)
+#define cr_det_out_set _setmask(CR_DET_OUT)
+#define cr_det_out_reset _resetmask(CR_DET_OUT)
+#define cr_det_out_power _powermask(CR_DET_OUT)
+
+#define CHARGE_PUMP_OUT 18 /*!< Enable external charge pump. */
+#define charge_pump_out _setmask(CHARGE_PUMP_OUT)
+#define charge_pump_out_ _resetmask(CHARGE_PUMP_OUT)
+#define charge_pump_out_set _setmask(CHARGE_PUMP_OUT)
+#define charge_pump_out_reset _resetmask(CHARGE_PUMP_OUT)
+#define charge_pump_out_power _powermask(CHARGE_PUMP_OUT)
+
+#define BDIS_ACON_OUT 19 /*!< Enable auto A-connect after B-disconnect. */
+#define bdis_acon_out _setmask(BDIS_ACON_OUT)
+#define bdis_acon_out_ _resetmask(BDIS_ACON_OUT)
+#define bdis_acon_out_set _setmask(BDIS_ACON_OUT)
+#define bdis_acon_out_reset _resetmask(BDIS_ACON_OUT)
+#define bdis_acon_out_power _powermask(BDIS_ACON_OUT)
+
+#define ID_PULLDOWN_OUT 20 /*!< Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */
+#define id_pulldown_out _setmask(ID_PULLDOWN_OUT)
+#define id_pulldown_out_ _resetmask(ID_PULLDOWN_OUT)
+#define id_pulldown_out_set _setmask(ID_PULLDOWN_OUT)
+#define id_pulldown_out_reset _resetmask(ID_PULLDOWN_OUT)
+#define id_pulldown_out_power _powermask(ID_PULLDOWN_OUT)
+
+#define UART_OUT 21 /*!< Enable Transparent UART mode (CEA-936.) */
+#define uart_out _setmask(UART_OUT)
+#define uart_out_ _resetmask(UART_OUT)
+#define uart_out_set _setmask(UART_OUT)
+#define uart_out_reset _resetmask(UART_OUT)
+#define uart_out_power _powermask(UART_OUT)
+
+#define AUDIO_OUT 22 /*!< Enable Audio mode (CEA-936 CarKit interrupt detector.) */
+#define audio_out _setmask(AUDIO_OUT)
+#define audio_out_ _resetmask(AUDIO_OUT)
+#define audio_out_set _setmask(AUDIO_OUT)
+#define audio_out_reset _resetmask(AUDIO_OUT)
+#define audio_out_power _powermask(AUDIO_OUT)
+
+#define MONO_OUT 23 /*!< Enable Mono-Audio mode (CEA-936.) */
+#define mono_out _setmask(MONO_OUT)
+#define mono_out_ _resetmask(MONO_OUT)
+#define mono_out_set _setmask(MONO_OUT)
+#define mono_out_reset _resetmask(MONO_OUT)
+#define mono_out_power _powermask(MONO_OUT)
+ /* @) */
+
+ /*! @name Peripheral Controller Driver Outputs
+ * @{
+ */
+
+#define REMOTE_WAKEUP_OUT 24 /*!< Peripheral will perform remote wakeup. */
+#define remote_wakeup_out _setmask(REMOTE_WAKEUP_OUT)
+#define remote_wakeup_out_ _resetmask(REMOTE_WAKEUP_OUT)
+#define remote_wakeup_out_set _setmask(REMOTE_WAKEUP_OUT)
+#define remote_wakeup_out_reset _resetmask(REMOTE_WAKEUP_OUT)
+#define remote_wakeup_out_power _powermask(REMOTE_WAKEUP_OUT)
+ /* @) */
+
+ /*! @name Host Controller Driver Outputs
+ * @{
+ */
+
+#define HCD_RH_OUT 25 /*!< Host will enable root hub */
+#define hcd_rh_out _setmask(HCD_RH_OUT)
+#define hcd_rh_out_ _resetmask(HCD_RH_OUT)
+#define hcd_rh_out_set _setmask(HCD_RH_OUT)
+#define hcd_rh_out_reset _resetmask(HCD_RH_OUT)
+#define hcd_rh_out_power _powermask(HCD_RH_OUT)
+
+#define LOC_SOF_OUT 26 /*!< Host will enable packet traffic. */
+#define loc_sof_out _setmask(LOC_SOF_OUT)
+#define loc_sof_out_ _resetmask(LOC_SOF_OUT)
+#define loc_sof_out_set _setmask(LOC_SOF_OUT)
+#define loc_sof_out_reset _resetmask(LOC_SOF_OUT)
+#define loc_sof_out_power _powermask(LOC_SOF_OUT)
+
+#define LOC_SUSPEND_OUT 27 /*!< Host will suspend bus. */
+#define loc_suspend_out _setmask(LOC_SUSPEND_OUT)
+#define loc_suspend_out_ _resetmask(LOC_SUSPEND_OUT)
+#define loc_suspend_out_set _setmask(LOC_SUSPEND_OUT)
+#define loc_suspend_out_reset _resetmask(LOC_SUSPEND_OUT)
+#define loc_suspend_out_power _powermask(LOC_SUSPEND_OUT)
+
+#define REMOTE_WAKEUP_EN_OUT 28 /*!< Host will send remote wakeup enable or disable request. */
+#define remote_wakeup_en_out _setmask(REMOTE_WAKEUP_EN_OUT)
+#define remote_wakeup_en_out_ _resetmask(REMOTE_WAKEUP_EN_OUT)
+#define remote_wakeup_en_out_set _setmask(REMOTE_WAKEUP_EN_OUT)
+#define remote_wakeup_en_out_reset _resetmask(REMOTE_WAKEUP_EN_OUT)
+#define remote_wakeup_en_out_power _powermask(REMOTE_WAKEUP_EN_OUT)
+
+#define HNP_EN_OUT 29 /*!< Host will send HNP enable request. */
+#define hnp_en_out _setmask(HNP_EN_OUT)
+#define hnp_en_out_ _resetmask(HNP_EN_OUT)
+#define hnp_en_out_set _setmask(HNP_EN_OUT)
+#define hnp_en_out_reset _resetmask(HNP_EN_OUT)
+#define hnp_en_out_power _powermask(HNP_EN_OUT)
+
+#define HPWR_OUT 30 /*!< Host will enable high power (external charge pump.) */
+#define hpwr_out _setmask(HPWR_OUT)
+#define hpwr_out_ _resetmask(HPWR_OUT)
+#define hpwr_out_set _setmask(HPWR_OUT)
+#define hpwr_out_reset _resetmask(HPWR_OUT)
+#define hpwr_out_power _powermask(HPWR_OUT)
+ /* @) */
+#define MAX_OUTPUTS 31
+
+/* Generated by otg-ioctls-h.awk
+ *
+ * Do not Edit this file,
+ */
+
+/* %Z %K */
+
+
+#if defined(OTG_LINUX)
+
+#define OTGADMIN_MAGIC 'O'
+#define OTGADMIN_VERSION _IOR(OTGADMIN_MAGIC, 1, u64)
+#define OTGADMIN_STATUS _IOR(OTGADMIN_MAGIC, 3, struct otg_status_update)
+#define OTGADMIN_SET_FUNCTION _IOW(OTGADMIN_MAGIC, 4, struct otg_admin_command)
+#define OTGADMIN_GET_FUNCTION _IOR(OTGADMIN_MAGIC, 4, struct otg_admin_command)
+#define OTGADMIN_SET_INFO _IOW(OTGADMIN_MAGIC, 5, struct otg_firmware_info)
+#define OTGADMIN_GET_INFO _IOR(OTGADMIN_MAGIC, 5, struct otg_firmware_info)
+#define OTGADMIN_SET_STATE _IOW(OTGADMIN_MAGIC, 6, struct otg_state)
+#define OTGADMIN_GET_STATE _IOR(OTGADMIN_MAGIC, 6, struct otg_state)
+#define OTGADMIN_SET_TEST _IOW(OTGADMIN_MAGIC, 7, struct otg_test)
+#define OTGADMIN_GET_TEST _IOR(OTGADMIN_MAGIC, 7, struct otg_test)
+#define OTGADMIN_SET_SERIAL _IOW(OTGADMIN_MAGIC, 8, struct otg_admin_command)
+#define OTGADMIN_GET_SERIAL _IOR(OTGADMIN_MAGIC, 8, struct otg_admin_command)
+
+
+
+ /*!
+ * Signal Sent: a_bcon_no_tmout_req
+ * State Valid: otg_host
+ * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode).
+ */
+#define OTGADMIN_A_BCON_NO_TMOUT_REQ _IOW(OTGADMIN_MAGIC, 10, int)
+
+ /*!
+ * Signal Sent: a_hpwr_req
+ * State Valid: otg_host
+ * Application on A-host wants external charge pump enabled.
+ */
+#define OTGADMIN_A_HPWR_REQ _IOW(OTGADMIN_MAGIC, 11, int)
+
+ /*!
+ * Signal Sent: bus_drop
+ * State Valid: otg_ok
+ * Application on Device needs to power down bus.
+ */
+#define OTGADMIN_BUS_DROP _IOW(OTGADMIN_MAGIC, 12, int)
+
+ /*!
+ * Signal Sent: a_bus_drop
+ * State Valid: otg_ok
+ * Application on A-Device needs to power down bus.
+ */
+#define OTGADMIN_A_BUS_DROP _IOW(OTGADMIN_MAGIC, 13, int)
+
+ /*!
+ * Signal Sent: b_bus_drop
+ * State Valid: otg_ok
+ * Application on B-Device needs to disconnect from bus.
+ */
+#define OTGADMIN_B_BUS_DROP _IOW(OTGADMIN_MAGIC, 14, int)
+
+ /*!
+ * Signal Sent: bus_req
+ * State Valid: otg_ok
+ * Application on Device wants to use the bus.
+ */
+#define OTGADMIN_BUS_REQ _IOW(OTGADMIN_MAGIC, 15, int)
+
+ /*!
+ * Signal Sent: a_bus_req
+ * State Valid: otg_ok
+ * Application on A-Device wants to act as host
+ */
+#define OTGADMIN_A_BUS_REQ _IOW(OTGADMIN_MAGIC, 16, int)
+
+ /*!
+ * Signal Sent: b_bus_req
+ * State Valid: otg_ok
+ * Application on B-Device wants to act as host
+ */
+#define OTGADMIN_B_BUS_REQ _IOW(OTGADMIN_MAGIC, 17, int)
+
+ /*!
+ * Signal Sent: b_sess_req
+ * State Valid: otg_ok
+ * Application on B-Device to perform SRP (alias for b_srp_req.)
+ */
+#define OTGADMIN_B_SESS_REQ _IOW(OTGADMIN_MAGIC, 18, int)
+
+ /*!
+ * Signal Sent: suspend_req
+ * State Valid: otg_host
+ * Application on Device requests bus be suspended (alias for a_bus_req/.)
+ */
+#define OTGADMIN_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 19, int)
+
+ /*!
+ * Signal Sent: a_suspend_req
+ * State Valid: otg_host
+ * Application on A-host requests bus be suspended (alias for a_bus_req/.)
+ */
+#define OTGADMIN_A_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 20, int)
+
+ /*!
+ * Signal Sent: b_suspend_req
+ * State Valid: otg_host
+ * Application on B-host requests bus be suspended (alias for b_bus_req/.)
+ */
+#define OTGADMIN_B_SUSPEND_REQ _IOW(OTGADMIN_MAGIC, 21, int)
+
+ /*!
+ * Signal Sent: set_remote_wakeup_cmd
+ * State Valid: a_host
+ * A-Device will send Remote Wakeup Enable Request
+ */
+#define OTGADMIN_SET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 22, int)
+
+ /*!
+ * Signal Sent: remote_wakeup_cmd
+ * State Valid: tr_configured
+ * B-Device will perform Remote Wakeup.
+ */
+#define OTGADMIN_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 23, int)
+
+ /*!
+ * Signal Sent: reset_remote_wakeup_cmd
+ * State Valid: a_host
+ * A-Device will send Remote Wakeup Disable Request
+ */
+#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD _IOW(OTGADMIN_MAGIC, 24, int)
+
+ /*!
+ * Signal Sent: clr_err_cmd
+ * State Valid: a_vbus_err
+ * A-Device ill clears Vbus overcurrent error.
+ */
+#define OTGADMIN_CLR_ERR_CMD _IOW(OTGADMIN_MAGIC, 25, int)
+
+ /*!
+ * Signal Sent: b_hnp_cmd
+ * State Valid: b_configured
+ * B-Device will attempt HNP
+ */
+#define OTGADMIN_B_HNP_CMD _IOW(OTGADMIN_MAGIC, 26, int)
+
+ /*!
+ * Signal Sent: ph_int_cmd
+ * State Valid: ph_audio
+ * B-Device will request Carkit interrupt
+ */
+#define OTGADMIN_PH_INT_CMD _IOW(OTGADMIN_MAGIC, 27, int)
+
+ /*!
+ * Signal Sent: ph_audio_cmd
+ * State Valid: ph_uart
+ * Application on B-Device connected to Carkit requests audio mode.
+ */
+#define OTGADMIN_PH_AUDIO_CMD _IOW(OTGADMIN_MAGIC, 28, int)
+
+ /*!
+ * Signal Sent: cr_int_cmd
+ * State Valid: cr_aud
+ * Application on A-Device wants to emulate Carkit
+ */
+#define OTGADMIN_CR_INT_CMD _IOW(OTGADMIN_MAGIC, 29, int)
+
+ /*!
+ * Signal Sent: led_on_cmd
+ * State Valid: ph_uart
+ * B-Device will enable Carkit LED
+ */
+#define OTGADMIN_LED_ON_CMD _IOW(OTGADMIN_MAGIC, 30, int)
+
+ /*!
+ * Signal Sent: led_off_cmd
+ * State Valid: ph_uart
+ * B-Device will disable Carkit LED
+ */
+#define OTGADMIN_LED_OFF_CMD _IOW(OTGADMIN_MAGIC, 31, int)
+
+ /*!
+ * Signal Sent: enable_otg
+ * State Valid: otg_all
+ * Move State Machine otg_disabled state.
+ */
+#define OTGADMIN_ENABLE_OTG _IOW(OTGADMIN_MAGIC, 32, int)
+
+#define OTGADMIN_MAXNR 33
+
+
+#endif /* defined(OTG_LINUX) */
+
+/* Generated by otg-iocontrol-h.awk
+ *
+ * Do not Edit this file,
+ */
+
+/* %Z %K */
+
+
+#if defined(OTG_WINCE)
+
+#define OTGADMIN_BASE$ 0x401
+#define _USBLAN_CTL_CODE(_Function, _Method, _Access) \
+ CTL_CODE(OTGADMIN_BASE, _Function, _Method, _Access)
+
+#define OTGADMIN_VERSION OTGADMIN_CTL_CODE(0x401, METHOD_BUFFERRED, FILE_READ_ACCESS)
+#define OTGADMIN_STATUS OTGADMIN_CTL_CODE(0x403, METHOD_BUFFERRED, FILE_READ_ACCESS)
+#define OTGADMIN_SET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_WRITE_ACCESS)
+#define OTGADMIN_GET_FUNCTION OTGADMIN_CTL_CODE(0x404, METHOD_BUFFERRED, FILE_READ_ACCESS)
+#define OTGADMIN_SET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_WRITE_ACCESS)
+#define OTGADMIN_GET_INFO OTGADMIN_CTL_CODE(0x405, METHOD_BUFFERRED, FILE_READ_ACCESS)
+#define OTGADMIN_SET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_WRITE_ACCESS)
+#define OTGADMIN_GET_STATE OTGADMIN_CTL_CODE(0x406, METHOD_BUFFERRED, FILE_READ_ACCESS)
+#define OTGADMIN_SET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_WRITE_ACCESS)
+#define OTGADMIN_GET_TEST OTGADMIN_CTL_CODE(0x407, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+
+
+ /*!
+ * Signal Sent: a_bcon_no_tmout_req
+ * State Valid: otg_host
+ * Application on A-host wants Ta_wait_bcon timeout disabled (non-OTG mode).
+ */
+#define OTGADMIN_A_BCON_NO_TMOUT_REQ OTGADMIN_CTL_CODE(, 0x40a, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: a_hpwr_req
+ * State Valid: otg_host
+ * Application on A-host wants external charge pump enabled.
+ */
+#define OTGADMIN_A_HPWR_REQ OTGADMIN_CTL_CODE(, 0x40b, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: bus_drop
+ * State Valid: otg_ok
+ * Application on Device needs to power down bus.
+ */
+#define OTGADMIN_BUS_DROP OTGADMIN_CTL_CODE(, 0x40c, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: a_bus_drop
+ * State Valid: otg_ok
+ * Application on A-Device needs to power down bus.
+ */
+#define OTGADMIN_A_BUS_DROP OTGADMIN_CTL_CODE(, 0x40d, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: b_bus_drop
+ * State Valid: otg_ok
+ * Application on B-Device needs to disconnect from bus.
+ */
+#define OTGADMIN_B_BUS_DROP OTGADMIN_CTL_CODE(, 0x40e, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: bus_req
+ * State Valid: otg_ok
+ * Application on Device wants to use the bus.
+ */
+#define OTGADMIN_BUS_REQ OTGADMIN_CTL_CODE(, 0x40f, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: a_bus_req
+ * State Valid: otg_ok
+ * Application on A-Device wants to act as host
+ */
+#define OTGADMIN_A_BUS_REQ OTGADMIN_CTL_CODE(, 0x410, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: b_bus_req
+ * State Valid: otg_ok
+ * Application on B-Device wants to act as host
+ */
+#define OTGADMIN_B_BUS_REQ OTGADMIN_CTL_CODE(, 0x411, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: b_sess_req
+ * State Valid: otg_ok
+ * Application on B-Device to perform SRP (alias for b_srp_req.)
+ */
+#define OTGADMIN_B_SESS_REQ OTGADMIN_CTL_CODE(, 0x412, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: suspend_req
+ * State Valid: otg_host
+ * Application on Device requests bus be suspended (alias for a_bus_req/.)
+ */
+#define OTGADMIN_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x413, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: a_suspend_req
+ * State Valid: otg_host
+ * Application on A-host requests bus be suspended (alias for a_bus_req/.)
+ */
+#define OTGADMIN_A_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x414, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: b_suspend_req
+ * State Valid: otg_host
+ * Application on B-host requests bus be suspended (alias for b_bus_req/.)
+ */
+#define OTGADMIN_B_SUSPEND_REQ OTGADMIN_CTL_CODE(, 0x415, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: set_remote_wakeup_cmd
+ * State Valid: a_host
+ * A-Device will send Remote Wakeup Enable Request
+ */
+#define OTGADMIN_SET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x416, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: remote_wakeup_cmd
+ * State Valid: tr_configured
+ * B-Device will perform Remote Wakeup.
+ */
+#define OTGADMIN_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x417, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: reset_remote_wakeup_cmd
+ * State Valid: a_host
+ * A-Device will send Remote Wakeup Disable Request
+ */
+#define OTGADMIN_RESET_REMOTE_WAKEUP_CMD OTGADMIN_CTL_CODE(, 0x418, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: clr_err_cmd
+ * State Valid: a_vbus_err
+ * A-Device ill clears Vbus overcurrent error.
+ */
+#define OTGADMIN_CLR_ERR_CMD OTGADMIN_CTL_CODE(, 0x419, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: b_hnp_cmd
+ * State Valid: b_configured
+ * B-Device will attempt HNP
+ */
+#define OTGADMIN_B_HNP_CMD OTGADMIN_CTL_CODE(, 0x41a, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: ph_int_cmd
+ * State Valid: ph_audio
+ * B-Device will request Carkit interrupt
+ */
+#define OTGADMIN_PH_INT_CMD OTGADMIN_CTL_CODE(, 0x41b, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: ph_audio_cmd
+ * State Valid: ph_uart
+ * Application on B-Device connected to Carkit requests audio mode.
+ */
+#define OTGADMIN_PH_AUDIO_CMD OTGADMIN_CTL_CODE(, 0x41c, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: cr_int_cmd
+ * State Valid: cr_aud
+ * Application on A-Device wants to emulate Carkit
+ */
+#define OTGADMIN_CR_INT_CMD OTGADMIN_CTL_CODE(, 0x41d, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: led_on_cmd
+ * State Valid: ph_uart
+ * B-Device will enable Carkit LED
+ */
+#define OTGADMIN_LED_ON_CMD OTGADMIN_CTL_CODE(, 0x41e, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: led_off_cmd
+ * State Valid: ph_uart
+ * B-Device will disable Carkit LED
+ */
+#define OTGADMIN_LED_OFF_CMD OTGADMIN_CTL_CODE(, 0x41f, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+ /*!
+ * Signal Sent: enable_otg
+ * State Valid: otg_all
+ * Move State Machine otg_disabled state.
+ */
+#define OTGADMIN_ENABLE_OTG OTGADMIN_CTL_CODE(, 0x420, METHOD_BUFFERRED, FILE_READ_ACCESS)
+
+#define OTGADMIN_MAXNR 33
+
+
+#endif /* defined(OTG_WINCE) */
+
+/* Generated by otg-metas-h.awk
+ *
+ * Do not Edit thie file
+ */
+
+/* %Z %K */
+
+
+ /*! @name OTG Figure 6-2 Meta States
+ *
+ * These are the Meta States defined in the 2.0 OTG Specification
+ * Figure 6.2 - Dual-Role A-Device.
+ * @{
+ */
+
+#define m_a_idle 0 /* */
+
+#define m_a_wait_vrise 1 /* */
+
+#define m_a_wait_bcon 2 /* */
+
+#define m_a_host 3 /* */
+
+#define m_a_suspend 4 /* */
+
+#define m_a_peripheral 5 /* */
+
+#define m_a_wait_vfall 6 /* */
+
+#define m_a_vbus_err 7 /* */
+ /* @} */
+ /*! @name OTG Figure 6-3 Meta States
+ *
+ * These are the Meta States defined in the 2.0 OTG Specification
+ * Figure 6.3 - Dual-Role B-Device.
+ * @{
+ */
+
+#define m_b_idle 8 /* */
+
+#define m_b_srp_init 9 /* */
+
+#define m_b_peripheral 10 /* */
+
+#define m_b_suspend 11 /* */
+
+#define m_b_wait_acon 12 /* */
+
+#define m_b_host 13 /* */
+
+#define m_b_suspended 14 /* */
+ /* @} */
+ /*! @name Carkit Meta States Figure 7-7
+ *
+ * @{
+ */
+
+#define m_ph_disc 15 /* Equivalent to b_peripheral */
+
+#define m_ph_init 16 /* */
+
+#define m_ph_uart 17 /* */
+
+#define m_ph_aud 18 /* */
+
+#define m_ph_wait 19 /* */
+
+#define m_ph_exit 20 /* */
+
+#define m_cr_init 21 /* */
+
+#define m_cr_uart 22 /* */
+
+#define m_cr_aud 23 /* */
+
+#define m_cr_ack 24 /* */
+
+#define m_cr_wait 25 /* */
+
+#define m_cr_disc 26 /* */
+ /* @} */
+ /*! @name Additional states used locally.
+ *
+ * @{
+ */
+
+#define m_otg_init 27 /* */
+
+#define m_usb_accessory 28 /* */
+
+#define m_usb_factory 29 /* */
+
+#define m_unknown 30 /* */
+ /* @} */
+
+#define OTG_METAS_FW 31
+
+extern char *otg_meta_names[];
diff --git a/drivers/otg/otg/otg-hcd.h b/drivers/otg/otg/otg-hcd.h
new file mode 100644
index 000000000000..9f0412d457d1
--- /dev/null
+++ b/drivers/otg/otg/otg-hcd.h
@@ -0,0 +1,102 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg-hcd.h - OTG Host Controller Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-hcd.h|20061218212925|63115
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*!
+ * @file otg/otg/otg-hcd.h
+ * @brief Defines common to On-The-Go Host Controller Support
+ *
+ * This file defines the hcd_ops and hcd_instance structures.
+ *
+ * The hcd_ops structure contains all of the output functions that will
+ * be called as required by the OTG event handler when changing states.
+ *
+ * The hcd_instance structure is used to maintain the global data
+ * required by the host controller drivers.
+ *
+ * @ingroup OTGAPI
+ * @ingroup HCD
+ */
+
+/*!
+ * @name HCD Host Controller Driver
+ * @{
+ */
+struct hcd_instance;
+
+/*!
+ * @struct hcd_ops
+ * The pcd_ops structure contains pointers to all of the functions implemented for the
+ * linked in driver. Having them in this structure allows us to easily determine what
+ * functions are available without resorting to ugly compile time macros or ifdefs
+ *
+ * There is only one instance of this, defined in the device specific lower layer.
+ */
+struct hcd_ops {
+
+ /* Driver Initialization - by degrees
+ */
+ int (*mod_init) (struct otg_instance *); /*!< HCD Module Initialization */
+ void (*mod_exit) (struct otg_instance *); /*!< HCD Module Exit */
+
+
+ /* mandatory */
+ int max_ports; /*!< maximum number of ports available */
+ u32 capabilities; /*!< UDC Capabilities - see usbd-bus.h for details */
+ char *name; /*!< name of controller */
+
+
+ otg_output_proc_t hcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */
+ otg_output_proc_t hcd_en_func; /*!< OTG calls to enable or disable the HCD */
+ otg_output_proc_t hcd_rh_func; /*!< OTG calls to enable HCD Root Hub */
+ otg_output_proc_t loc_sof_func; /*!< OTG calls to into a_host or b_host state - attempt to use port */
+ otg_output_proc_t loc_suspend_func; /*!< OTG calls to suspend bus */
+ otg_output_proc_t remote_wakeup_en_func; /*!< OTG calls to issue SET FEATURE REMOTE WAKEUP */
+ otg_output_proc_t hnp_en_func; /*!< OTG calls to issues SET FEATURE B_HNP_ENABLE */
+
+ framenum_t framenum; /*!< OTG calls to get current USB Frame number */
+};
+
+/*!
+ * @struct hcd_instance otg-hcd.h "otg/otg-hcd.h"
+ */
+struct hcd_instance {
+ struct otg_instance *otg; /*!< pointer to OTG Instance */
+ otg_tag_t TAG;
+ void * privdata; /*!< pointer to private data for PCD */
+ //struct WORK_STRUCT bh; /*!< work structure for bottom half handler */
+ int active;
+};
+
+#define HCD hcd_trace_tag
+extern otg_tag_t HCD;
+extern struct hcd_ops hcd_ops;
+extern struct hcd_instance *hcd_instance;
+#if !defined(OTG_C99)
+extern void fs_hcd_global_init(void);
+#endif /* !defined(OTG_C99) */
+extern void hcd_init_func(struct otg_instance *, u8 );
+extern void hcd_en_func(struct otg_instance *, u8 );
+
+/* @} */
diff --git a/drivers/otg/otg/otg-linux.h b/drivers/otg/otg/otg-linux.h
new file mode 100644
index 000000000000..48c2a336bd39
--- /dev/null
+++ b/drivers/otg/otg/otg-linux.h
@@ -0,0 +1,1384 @@
+/*
+ * 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
+ */
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-linux.h
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/otg/linux/otg-linux.h|20070711184304|55012
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+
+/*!
+ * @file otg/otg/otg-linux.h
+ * @brief Linux OS Compatibility defines
+ *
+ * See otg-os.h for API definitions.
+ *
+ * @ingroup OSAPI
+ * @ingroup LINUXAPI
+ */
+#ifndef _OTG_LINUX_H
+#define _OTG_LINUX_H 1
+
+#if !defined(_OTG_MODULE_H)
+#include <otg/otg-module.h>
+#endif
+
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+#if !defined(LINUX26) && !defined(LINUX24)
+#error "One of LINUX24 and LINUX26 needs to be defined here"
+#endif
+
+#if defined(LINUX26)
+#include <linux/device.h>
+#endif
+
+
+#undef OTG_SKYE_LED
+#ifdef OTG_SKYE_LED
+#include <asm/arch/gpio.h>
+#define LED1 SP_GP_SP_A26
+#define LED2 SP_GP_SP_A7
+void otg_led(int led, int flag);
+void otg_led_init(int led);
+
+//#define LEDI LED1 /* define this to get local_irq_save/restore */
+#define LEDI 0
+
+#else
+#define LED1 0
+#define LED2 1
+#define LEDI 0
+void otg_led(int led, int flag);
+void otg_led_init(int led);
+#endif
+
+/* ********************************************************************************************** */
+
+#define CONFIG_OTG_LNX 1
+
+/*! @name Compilation Related
+ */
+
+/*@{*/
+#undef PRAGMAPACK
+/** Macros to support packed structures **/
+#define PACKED1 __attribute__((packed))
+#define PACKED2
+#define PACKED0
+
+/** Macros to support packed enum's **/
+#define PACKED_ENUM enum
+#define PACKED_ENUM_EXTRA
+
+#define INLINE __inline__
+
+/*@}*/
+
+/*! @name Lock Interrupts
+ */
+
+/*@{*/
+
+static inline void otg_disable_interrupts(void)
+{
+ /* do nothing */
+}
+
+static inline void otg_enable_interrupts(void)
+{
+ /* do nothing */
+}
+
+
+/*@}*/
+
+
+/* ********************************************************************************************** */
+/* otg-trace - included so that TRACE_MSG can be used if desired to debug
+ * the otg-xxx implementation
+ */
+
+typedef u32 otg_tick_t;
+#include <otg/otg-trace.h>
+
+
+/* ********************************************************************************************** */
+/*!
+ * @name Semaphore
+ *
+ * Posix compatible Semaphores.
+ * @{
+ */
+//typedef struct semaphore otg_sem_t;
+
+typedef struct xotg_sem {
+ char *name;
+ BOOL semdebug;
+ struct semaphore sem;
+} otg_sem_t;
+
+
+static int inline otg_sem_init(char *name, otg_sem_t *sem, int pshared, unsigned value) {
+ sem->name = name;
+ sem->semdebug = FALSE;
+ sema_init(&sem->sem, value);
+ return 0;
+}
+
+static int inline otg_sem_init_locked(char *name, otg_sem_t *sem) {
+ otg_sem_init(name, sem, 1, 0);
+ //sema_init(sem, 0);
+ return 0;
+}
+
+static int inline otg_sem_init_unlocked(char *name, otg_sem_t *sem) {
+ otg_sem_init(name, sem, 1, 1);
+ //sema_init(sem, 1);
+ return 0;
+}
+
+static int inline otg_sem_destroy(otg_sem_t *sem) {
+ return 0;
+}
+static int inline otg_sem_wait(otg_sem_t *sem) {
+ return down_interruptible(&sem->sem);
+}
+static int inline otg_sem_trywait(otg_sem_t *sem) {
+ return down_trylock(&sem->sem);
+}
+static int inline otg_sem_post(otg_sem_t *sem) {
+ up(&sem->sem);
+ return 0;
+}
+
+/*! @} */
+
+
+/* ********************************************************************************************** */
+/*!
+ * @name Mutex
+ *
+ * Posix compatible Mutexes.
+ * @{
+ */
+typedef unsigned long otg_pthread_mutex_t;
+
+static int inline otg_pthread_mutex_lock(otg_pthread_mutex_t *mutex) {
+ local_irq_save(*mutex);
+ return 0;
+}
+
+static int inline otg_pthread_mutex_trylock(otg_pthread_mutex_t *mutex) {
+ local_irq_save(*mutex);
+ return 0;
+}
+static int inline otg_pthread_mutex_unlock(otg_pthread_mutex_t *mutex) {
+ local_irq_restore(*mutex);
+ return 0;
+}
+
+
+/* ********************************************************************************************** */
+/*! @} */
+
+/*!
+ * @name Atomic
+ *
+ * Basic Atomic operations.
+ * @{
+ */
+typedef atomic_t otg_atomic_t;
+
+static void inline otg_atomic_set(otg_atomic_t *a, int b) {
+ atomic_set(a, b);
+}
+static void inline otg_atomic_add(int a, otg_atomic_t *b) {
+ atomic_add(a, b);
+}
+static void inline otg_atomic_sub(int a, otg_atomic_t *b) {
+ atomic_sub(a, b);
+}
+static void inline otg_atomic_clr(otg_atomic_t *a) {
+ atomic_set(a, 0);
+}
+static void inline otg_atomic_inc(otg_atomic_t *a) {
+ return atomic_inc(a);
+}
+static void inline otg_atomic_dec(otg_atomic_t *a) {
+ return atomic_dec(a);
+}
+static int inline otg_atomic_read(otg_atomic_t *a) {
+ return atomic_read(a);
+}
+
+static __inline__ int atomic_post_inc(volatile otg_atomic_t *v) {
+ unsigned long flags;
+ int result;
+ local_irq_save(flags);
+ result = (v->counter)++;
+ local_irq_restore(flags);
+ return(result);
+}
+
+static __inline__ int atomic_pre_dec(volatile otg_atomic_t *v) {
+ unsigned long flags;
+ int result;
+ local_irq_save(flags);
+ result = --(v->counter);
+ local_irq_restore(flags);
+ return(result);
+}
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name Time of Day
+ * gettimeofday
+ * @{
+ */
+static int inline otg_gettimeofday(struct timeval *tv)
+{
+ do_gettimeofday(tv);
+ return 0;
+}
+
+
+static void inline otg_get_random_bytes(u8 *p, int n)
+{
+ get_random_bytes(p, n);
+}
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name Memory Allocation
+ *
+ * Simple allocated memory operations.
+ * @{
+ */
+
+void * otg_cmalloc(int n);
+void otg_free(void *);
+
+#define KMALLOC(n) _kmalloc(__FUNCTION__, __LINE__, n, GFP_ATOMIC)
+#define CKMALLOC(n) _ckmalloc(__FUNCTION__, __LINE__, n, GFP_ATOMIC)
+#define LSTRDUP(str) _lstrdup(__FUNCTION__, __LINE__, str)
+#define LKFREE(p) _lkfree(__FUNCTION__, __LINE__, p)
+
+//#define kmalloc(n) _kmalloc(__FUNCTION__, __LINE__, n, GFP_ATOMIC)
+#define ckmalloc(n) _ckmalloc(__FUNCTION__, __LINE__, n, GFP_ATOMIC)
+#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str)
+#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p)
+
+#define OTG_MALLOC_TEST
+#undef OTG_MALLOC_DEBUG
+//#define OTG_MALLOC_DEBUG
+#ifdef OTG_MALLOC_TEST
+ extern int otg_mallocs;
+#endif
+
+
+static inline void *_kmalloc (const char *func, int line, int n, int f)
+{
+ void *p;
+ if ((p = kmalloc (n, f)) == NULL) {
+ return NULL;
+ }
+ #ifdef OTG_MALLOC_TEST
+ ++otg_mallocs;
+ #endif
+ #ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs);
+ #endif
+ return p;
+}
+
+static inline void *_ckmalloc (const char *func, int line, int n, int f)
+{
+ void *p;
+ if ((p = kmalloc (n, f)) == NULL) {
+ return NULL;
+ }
+ memset (p, 0, n);
+ #ifdef OTG_MALLOC_TEST
+ ++otg_mallocs;
+ #endif
+ #ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs);
+ #endif
+ return p;
+}
+
+static inline char *_lstrdup (const char *func, int line, char *str)
+{
+ int n;
+ char *s;
+ if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) {
+#ifdef OTG_MALLOC_TEST
+ ++otg_mallocs;
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, otg_mallocs);
+#endif
+ return strcpy (s, str);
+ }
+ return NULL;
+}
+
+static inline void _lkfree (const char *func, int line, void *p)
+{
+ if (p) {
+#ifdef OTG_MALLOC_TEST
+ --otg_mallocs;
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s:--1 %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs);
+#endif
+ kfree (p);
+#ifdef MALLOC_TEST
+ if (otg_mallocs < 0) {
+ printk(KERN_INFO"%s: %p %s %d %d otg_mallocs less zero!\n", __FUNCTION__, p, func, line, otg_mallocs);
+ }
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ if (otg_mallocs >= 0) {
+ printk(KERN_INFO"%s:--2 %s %d NULL\n", __FUNCTION__, func, line);
+ }
+#endif
+ }
+}
+
+
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name Task Allocation
+ *
+ * Long lived, low priority, high latency tasks.
+ *
+ * Tasks may use semaphores, mutexes, atomic operations and memory allocations.
+ * @{
+ *
+ * TASKS are long lived, MAY HAVE low priority and high latency, the typical
+ * implementation has the work item wait in a semaphore waiting for work. :w
+ *
+ * The TASK work functions are structured to run until terminated, using
+ * a semaphore to wait for work.
+ *
+ * TASK creation:
+ * struct otg_task *task = otg_task_init("taskname", task_work, TAG);
+ * if (task)
+ * otg_task_start(task);
+ *
+ * TASK termination:
+ * otg_task_exit(task);
+ *
+ * TASK work notification
+ * otg_up_work(task);
+ *
+ * TASK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_task *task = (struct otg_task *)data;
+ * void *mydata = task->data;
+ * otg_up_admin(task);
+ * do {
+ * // wait for work
+ * otg_down_work(task);
+ * // do work
+ * } while (!task->terminating);
+ * task->terminated = TRUE;
+ * otg_up_admin(task);
+ * }
+ *
+ * static struct otg_task *otg_task_init(char *name, void (*work) (void *), void *data, otg_tag_t tag)
+ * static void otg_up_work(struct otg_task *task)
+ * static void otg_up_admin(struct otg_task *task)
+ * static void otg_down_work(struct otg_task *task)
+ * static void otg_down_admin(struct otg_task *task)
+ * static void otg_task_start(struct otg_task *task)
+ * static void otg_task_exit(struct otg_task *task)
+ *
+ */
+
+/* XXX CONFIG_OTG_TASK_WORK
+ *
+ * Linux Implementation Configuration
+ *
+ * #define CONFIG_OTG_TASK_WORK
+ * Use work items only for TASK implemenation
+ */
+
+#if defined(LINUX26)
+ #define SCHEDULE_WORK(item) schedule_work(&item)
+#else /* LINUX26 */
+ #define SCHEDULE_WORK(item) schedule_work(&item)
+#endif /* LINUX26 */
+
+/*! struct otg_task
+ */
+typedef void *otg_task_arg_t;
+typedef void *(* otg_task_proc_t) (otg_task_arg_t data);
+struct otg_task {
+ #if !defined(CONFIG_OTG_TASK_WORK)
+ struct workqueue_struct *work_queue;
+ otg_sem_t admin_sem;
+ otg_sem_t work_sem;
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+
+ #if defined(LINUX26)
+ struct work_struct work;
+ #else /* LINUX26 */
+ struct work_struct work;
+ #endif /* LINUX26 */
+ otg_task_proc_t proc;
+ BOOL terminate;
+ BOOL terminated;
+ otg_task_arg_t *data;
+ BOOL taskdebug;
+ otg_tag_t tag;
+ char *name;
+};
+
+static void inline otg_sleep(int n)
+{
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout( n * HZ );
+}
+
+/*! otg_up_admin
+ * @brief Signal start task to re-start.
+ * @param task - otg_task instance pointer
+ */
+static void inline otg_up_admin(struct otg_task *task)
+{
+ #if defined(CONFIG_OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "UP ADMIN: %s", task->name);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ otg_sem_post(&task->admin_sem);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+}
+
+/*! otg_down_work
+ *@brief Used by work task to wait.
+ *@param task - otg_task instance pointer
+ */
+static void inline otg_down_work(struct otg_task *task)
+{
+ #if defined(CONFIG_OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN WORK: %s", task->name);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ while (otg_sem_wait(&task->work_sem));
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+/*! otg_task_proc - otg task handler
+ * @param data - otg_task instance pointer
+ */
+static void inline otg_task_proc(otg_task_arg_t data)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+/*! otg_task_proc - otg task handler
+ * @param work - otg_task instance pointer
+ */
+static void inline otg_task_proc(struct work_struct *work)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ struct otg_task *task = (struct otg_task *)data;
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ struct otg_task *task = (struct otg_task *)container_of(work, struct otg_task, work);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ otg_up_admin(task);
+ do {
+ // wait for work
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: DOWN %s\n", __FUNCTION__, task->name);
+
+ otg_down_work(task);
+
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: WORKING %s\n", __FUNCTION__, task->name);
+
+ task->proc(task->data);
+ } while (!task->terminate);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: TERMINATING %s\n", __FUNCTION__, task->name);
+ task->terminated = TRUE;
+ otg_up_admin(task);
+}
+
+/*! otg_task_init
+ *@brief Create otg task structure, create workqueue, initialize it.
+ *@param name - name of task or workqueue
+ *@param proc - handler
+ *@param data - parameter pointer for handler
+ *@param tag-
+ *@return initialized otg_task instance pointer
+ */
+static inline struct otg_task *otg_task_init2(char *name, otg_task_proc_t proc, otg_task_arg_t data, otg_tag_t tag)
+{
+ struct otg_task *task;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+
+ RETURN_NULL_UNLESS((task = CKMALLOC(sizeof (struct otg_task))));
+
+ task->tag = tag;
+ task->data = data;
+ task->name = name;
+ task->proc = proc;
+
+ #if defined(CONFIG_OTG_TASK_WORK)
+ task->terminated = task->terminate = TRUE;
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ task->terminated = task->terminate = FALSE;
+ #if defined(LINUX26)
+ THROW_UNLESS((task->work_queue = create_singlethread_workqueue(name)), error);
+ #else /* LINUX26 */
+ THROW_UNLESS((task->work_queue = create_workqueue(name)), error);
+ #endif /* LINUX26 */
+ //init_MUTEX_LOCKED(&task->admin_sem);
+ //init_MUTEX_LOCKED(&task->work_sem);
+ otg_sem_init_locked(name, &task->admin_sem);
+ otg_sem_init_locked(name, &task->work_sem);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&task->work, otg_task_proc, task);
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ INIT_WORK(&task->work, otg_task_proc);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+
+ return task;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (task) LKFREE(task);
+ return NULL;
+ }
+}
+
+
+/*! otg_up_work
+ * @brief Signal work param taskto re-start.
+ * @param task - otg_task instance pointer
+ */
+static void inline otg_up_work(struct otg_task *task)
+{
+ //TRACE_STRING(task->tag, "UP WORK: %s", task->name);
+
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(CONFIG_OTG_TASK_WORK)
+ //printk(KERN_INFO"%s: AAAA\n", __FUNCTION__);
+ task->terminated = FALSE;
+ SCHEDULE_WORK(task->work);
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ //printk(KERN_INFO"%s: BBBB\n", __FUNCTION__);
+ otg_sem_post(&task->work_sem);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+}
+
+/*! otg_down_admin
+ * @brief Used by admin task to wait.
+ * @param task - otg_task instance pointer
+ */
+static void inline otg_down_admin(struct otg_task *task)
+{
+ #if defined(CONFIG_OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN ADMIN: %s", task->name);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ while (otg_sem_wait(&task->admin_sem));
+ //DOWN(&task->admin_sem);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+}
+
+/*! otg_task_start
+ * @brief Start and wait for otg task.
+ * @param task - otg_task instance pointer
+ */
+static void inline otg_task_start(struct otg_task *task)
+{
+ #if defined(CONFIG_OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "START: %s", task->name);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ /* schedule work item and wait until it starts */
+ queue_work(task->work_queue, &task->work);
+ otg_down_admin(task);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+}
+
+/*! otg_task_exit
+ * @brief Terminate and wait for otg task.
+ * @param task - otg_task instance pointer
+ */
+static void inline otg_task_exit(struct otg_task *task)
+{
+ TRACE_STRING(task->tag, "EXIT: %s", task->name);
+ if (task->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(CONFIG_OTG_TASK_WORK)
+ while (!task->terminated) {
+ otg_sleep(1);
+ }
+ #else /* defined(CONFIG_OTG_TASK_WORK) */
+ /* signal termination */
+ task->terminate = TRUE;
+ otg_up_work(task);
+ otg_down_admin(task);
+
+ /* destroy workqueue */
+ flush_workqueue(task->work_queue);
+ destroy_workqueue(task->work_queue);
+ #endif /* defined(CONFIG_OTG_TASK_WORK) */
+
+ LKFREE(task);
+}
+
+
+/* ********************************************************************************************** */
+/*!
+ * @name WorkItem Allocation
+ *
+ * Short lived, low priority, high latency task.
+ *
+ * @{
+ *
+ * Work items are similar to tasks except that they are not expected
+ * to run for long periods of time or wait.
+ *
+ * A Work item function is run once and exits when the work
+ * is finished.
+ *
+ * static struct otg_workitem *otg_workitem_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_workitem_start(struct otg_workitem *tasklet)
+ * static void otg_workitem_exit(struct otg_workitem *tasklet)
+ *
+ * WORK creation:
+ * struct otg_workitem *tasklet = otg_workitem_init("taskletname", tasklet_work, TAG);
+ *
+ * WORK start:
+ * otg_workitem_start(tasklet);
+ *
+ * WORK termination:
+ * otg_workitem_exit(tasklet);
+ *
+ * WORK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_workitem *tasklet = (struct otg_workitem *)data;
+ * void *mydata = task->data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+
+/*! struct otg_workitem
+ */
+typedef void *otg_workitem_arg_t;
+typedef void *(* otg_workitem_proc_t) (otg_task_arg_t data);
+
+struct otg_workitem {
+
+ #if defined(LINUX26)
+ struct workqueue_struct *work_queue;
+ #endif /* defined(LINUX26) */
+
+ struct work_struct work;
+ otg_workitem_proc_t proc;
+ BOOL terminate;
+ BOOL terminated;
+ otg_task_arg_t *data;
+ BOOL workdebug;
+ otg_tag_t tag;
+ char *name;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+/*! otg_workitem_run
+ * @param data - workitem poiner
+ */
+static inline void otg_workitem_run(void *data)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+/*! otg_workitem_run
+ * @param work - work struct poiner
+ */
+static inline void otg_workitem_run(struct work_struct *work)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+{
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ struct otg_workitem *workitem = (struct otg_workitem *)data;
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ struct otg_workitem *workitem = (struct otg_workitem *)container_of(work, struct otg_workitem, work);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, workitem->name);
+
+ workitem->proc(workitem->data); // XXX convert to workitem->data
+ workitem->terminated = TRUE;
+
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s finished\n", __FUNCTION__, workitem->name);
+
+}
+
+/*! otg_workitem_init
+ * @brief Create otg work structure, create workqueue, initialize it.
+ * @param name - workitem name
+ * @param proc - workitem handler
+ * @param data - workitem data
+ * @param tag - otg tag
+ * @return otg_workitem instance pointer
+ */
+static inline struct otg_workitem *otg_workitem_init(char *name, otg_workitem_proc_t proc, otg_workitem_arg_t data, otg_tag_t tag)
+{
+ struct otg_workitem *workitem;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+
+ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((workitem = CKMALLOC(sizeof (struct otg_workitem))));
+
+ workitem->data = data;
+ workitem->tag = tag;
+ workitem->name = name;
+ workitem->proc = proc;
+ //workitem->workdebug = TRUE;
+
+ workitem->terminated = workitem->terminate = TRUE;
+
+ #if defined(LINUX26)
+ THROW_UNLESS((workitem->work_queue = create_singlethread_workqueue(name)), error);
+ #endif /* LINUX26 */
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ INIT_WORK(&workitem->work, otg_workitem_run, workitem);
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ INIT_WORK(&workitem->work, otg_workitem_run);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+
+ return workitem;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (workitem) LKFREE(workitem);
+ return NULL;
+ }
+}
+
+/*! otg_workitem_start
+ * @brief Signal work work to run.
+ * @param workitem - workitem pointer
+ */
+static void inline otg_workitem_start(struct otg_workitem *workitem)
+{
+ //TRACE_STRING(workitem->tag, "START: %s", workitem->name);
+
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, workitem->name);
+
+ //workitem->proc(workitem->data);
+ workitem->terminated = FALSE;
+ #if defined(LINUX26)
+ queue_work(workitem->work_queue,&workitem->work);
+ #else /* defined(LINUX26) */
+ SCHEDULE_WORK(workitem->work);
+ #endif /* defined(LINUX26) */
+}
+
+/*! otg_workitem_exit
+ * @brief Terminate and wait for otg work.
+ * @param workitem - workitem pointer
+ */
+static void inline otg_workitem_exit(struct otg_workitem *workitem)
+{
+ //TRACE_STRING(workitem->tag, "EXIT: %s", workitem->name);
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s terminating\n", __FUNCTION__, workitem->name);
+
+ while (!workitem->terminated) {
+ otg_sleep(1);
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s while loop terminating\n", __FUNCTION__, workitem->name);
+ }
+
+ if (workitem->workdebug)
+ printk(KERN_INFO"%s: %s terminated\n", __FUNCTION__, workitem->name);
+#if defined(LINUX26)
+ /* destroy workqueue */
+ flush_workqueue(workitem->work_queue);
+ destroy_workqueue(workitem->work_queue);
+#endif /* LINUX26 */
+ LKFREE(workitem);
+}
+
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name Tasklet Allocation
+ *
+ * Short lived, high priority, low latency tasklets.
+ *
+ * Tasklets MAY NOT use memory allocation or wait on semaphores.
+ *
+ * Taskslets may use signal semaphores, mutexes and atomic operations.
+ * @{
+ *
+ * TASKLETS are short lived, MUST run with low latency and SHOULD have high
+ * priority.
+ *
+ * The TASKLET work function is structure to run once and exit when the work
+ * is finished.
+ *
+ * static struct otg_tasklet *otg_tasklet_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_tasklet_start(struct otg_tasklet *tasklet)
+ * static void otg_tasklet_exit(struct otg_tasklet *tasklet)
+ *
+ * TASKLET creation:
+ * struct otg_tasklet *tasklet = otg_tasklet_init("taskletname", tasklet_work, TAG);
+ *
+ * TASKLET start:
+ * otg_tasklet_start(tasklet);
+ *
+ * TASKLET termination:
+ * otg_tasklet_exit(tasklet);
+ *
+ * TASKLET work process
+ *
+ * void task_work(otg_tasklet_arg_t data)
+ * {
+ * void *mydata = data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+/* XXX CONFIG_OTG_TASKLET_WORK
+ *
+ * Linux Implementation Configuration
+ *
+ * #define CONFIG_OTG_TASKLET_WORK
+ * Use work items only for TASK implemenation
+ *
+ * N.B. the Linux work item implementation for tasklets is not suitable
+ * for full OTG Dual-Role implemenation.
+ *
+ */
+
+/*! struct otg_tasklet
+ */
+#ifdef CONFIG_OTG_TASKLET_WORK
+typedef void *otg_tasklet_arg_t;
+#else /* CONFIG_OTG_TASKLET_WORK */
+typedef unsigned long otg_tasklet_arg_t;
+#endif /* CONFIG_OTG_TASKLET_WORK */
+typedef void *(* otg_tasklet_proc_t) (otg_tasklet_arg_t data);
+struct otg_tasklet {
+ #ifdef CONFIG_OTG_TASKLET_WORK
+ struct work_struct work;
+ #else /* CONFIG_OTG_TASKLET_WORK */
+ struct tasklet_struct tasklet;
+ #endif /* CONFIG_OTG_TASKLET_WORK */
+ BOOL terminated;
+ otg_tasklet_proc_t proc;
+ otg_tasklet_arg_t data;
+ BOOL taskdebug;
+ otg_tag_t tag;
+ char *name;
+};
+
+/*! otg_tasklet_run
+ * @param data - otg_tasklet pointer
+ */
+#ifdef CONFIG_OTG_TASKLET_WORK
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static inline void otg_tasklet_run(otg_tasklet_arg_t data)
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+static inline void otg_tasklet_run(struct work_struct *work)
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+#else /* CONFIG_OTG_TASKLET_WORK */
+static inline void otg_tasklet_run(otg_tasklet_arg_t data)
+#endif /* CONFIG_OTG_TASKLET_WORK */
+{
+ #ifdef CONFIG_OTG_TASKLET_WORK
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ struct otg_tasklet *tasklet = (struct otg_tasklet *)data;
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ struct otg_tasklet *tasklet = (struct otg_tasklet *)container_of(work, struct otg_tasklet, work);
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ #else /* CONFIG_OTG_TASKLET_WORK */
+ struct otg_tasklet *tasklet = (struct otg_tasklet *)data;
+ #endif /* CONFIG_OTG_TASKLET_WORK */
+
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ tasklet->proc(tasklet->data);
+ tasklet->terminated = TRUE;
+
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: %s finished\n", __FUNCTION__, tasklet->name);
+}
+
+/*! otg_tasklet_init
+ * @brief Create otg task structure, create workqueue, initialize it.
+ * @param name - otg_tasklet name
+ * @param proc - otg_tasklet process
+ * @param data - otg_taskle data, argument for proc
+ * @param tag - otg_tasklet tag
+ * @return initialized otg_tasklet instance pointer
+ */
+static inline struct otg_tasklet *otg_tasklet_init(char *name, otg_tasklet_proc_t proc, otg_tasklet_arg_t data, otg_tag_t tag)
+{
+ struct otg_tasklet *tasklet;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((tasklet = CKMALLOC(sizeof (struct otg_tasklet))));
+
+ tasklet->tag = tag;
+ tasklet->name = name;
+ tasklet->terminated = TRUE;
+ tasklet->proc = proc;
+ tasklet->data = data;
+
+ #ifdef CONFIG_OTG_TASKLET_WORK
+ INIT_WORK(&tasklet->work, otg_tasklet_run, tasklet);
+ #else /* CONFIG_OTG_TASKLET_WORK */
+ tasklet_init(&tasklet->tasklet, otg_tasklet_run, (otg_tasklet_arg_t) tasklet);
+ #endif /* CONFIG_OTG_TASKLET_WORK */
+
+ return tasklet;
+
+ CATCH(error) {
+ if (tasklet) LKFREE(tasklet);
+ return NULL;
+ }
+}
+
+/*! otg_tasklet_start
+ * @brief Signal work task to re-start.
+ * @param tasklet - otg_tasklet instance pointer
+ */
+static void inline otg_tasklet_start(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "START: %s", name);
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ tasklet->terminated = FALSE;
+ #ifdef CONFIG_OTG_TASKLET_WORK
+ SCHEDULE_WORK(tasklet->work);
+ #else /* CONFIG_OTG_TASKLET_WORK */
+ tasklet_schedule(&tasklet->tasklet);
+ #endif /* CONFIG_OTG_TASKLET_WORK */
+}
+
+/*! otg_tasklet_exit
+ * @brief Terminate and wait for otg task.
+ * @param tasklet - otg_tasklet instance pointer
+ */
+static void inline otg_tasklet_exit(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "EXIT: %s", name);
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ #ifdef CONFIG_OTG_TASKLET_WORK
+ while (!tasklet->terminated) {
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: SLEEPING\n", __FUNCTION__);
+ otg_sleep(1);
+ if (tasklet->taskdebug)
+ printk(KERN_INFO"%s: RUNNING\n", __FUNCTION__);
+ }
+ #else /* CONFIG_OTG_TASKLET_WORK */
+ tasklet_kill(&tasklet->tasklet);
+ #endif /* CONFIG_OTG_TASKLET_WORK */
+
+ LKFREE(tasklet);
+}
+
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name DMA Cache
+ *
+ * @{
+ *
+ *
+ * #define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+ * #define CACHE_SYNC_TX(buf, len) consistent_sync (NULL, buf, len, PCI_DMA_TODEVICE)
+ */
+/*! @} */
+#define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+// XXX define CONFIG_OTG_LD_TX_CACHE if you are on an older kernel that uses the
+// old consistent_sync() api
+//
+//#define CONFIG_OTG_OLD_TX_CACHE 0
+//
+#if defined(CONFIG_OTG_OLD_TX_CACHE)
+#define CACHE_SYNC_TX(buf, len) consistent_sync (NULL, buf, len, PCI_DMA_TODEVICE)
+#else
+#define CACHE_SYNC_TX(buf, len) consistent_sync (buf, len, PCI_DMA_TODEVICE)
+#endif
+
+/*@}*/
+
+
+
+/* ********************************************************************************************** */
+/*! @name likely and unlikely
+ *
+ * Under linux these can be used to provide a hint to GCC that
+ * the expression being evaluated is probably going to evaluate
+ * to true (likely) or false (unlikely). The intention is that
+ * GCC can then provide optimal code for that.
+ * @{
+ */
+#ifndef likely
+#define likely(x) x
+#endif
+#define otg_likely(x) likely(x)
+
+#ifndef unlikely
+#define unlikely(x) x
+#endif
+#define otg_unlikely(x) unlikely(x)
+/* @} */
+
+
+
+
+/* ********************************************************************************************** */
+/*
+ * XXX Deprecated and/or move to platform or architecture specific
+ */
+
+
+
+// Common to all supported versions of Linux ??
+
+#if 0
+#include <linux/list.h>
+#define otg_list_node list_head
+#define LIST_NODE struct list_head
+#define LIST_NODE_INIT(name) struct list_head name = {&name, &name}
+#define INIT_LIST_NODE(ptr) INIT_LIST_HEAD(ptr)
+#define LIST_ENTRY(pointer, type, member) list_entry(pointer, type, member)
+#define LIST_FOR_EACH(cursor, head) list_for_each(cursor, head)
+#define LIST_ADD_TAIL(n,h) list_add_tail(n,h)
+#define LIST_DEL(h) list_del(&(h))
+#else
+#include <otg/otg-list.h>
+#endif
+
+/*! @} */
+
+
+/*!@name Scheduling Primitives
+ *
+ * WORK_STRUCT
+ * WORK_ITEM
+ *
+ * SET_WORK_ARG()\n
+ * SCHEDULE_IMMEDIATE_WORK()\n
+ * NO_WORK_DATA()\n
+ * MOD_DEC_USE_COUNT\n
+ * MOD_INC_USE_COUNT\n
+ */
+
+/*! @{ */
+
+
+/* Separate Linux 2.4 and 2.6 versions of scheduling primitives */
+#if defined(LINUX26)
+ #include <linux/workqueue.h>
+
+ #define OLD_WORK_STRUCT work_struct
+ #define WORK_ITEM work_struct
+ #define OLD_WORK_ITEM work_struct
+ typedef struct OLD_WORK_ITEM OLD_WORK_ITEM;
+ #if 0
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) INIT_WORK((__item),(__routine),(__data))
+ #else
+ #include <linux/interrupt.h>
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) __prepare_work(&(__item),(__routine),(__data))
+ static inline void __prepare_work(struct work_struct *_work,
+ void (*_routine),
+ void * _data){
+ INIT_LIST_HEAD(&_work->entry);
+ _work->func = _routine;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ _work->data = _data;
+ _work->pending = 0;
+ init_timer(&_work->timer);
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ *work_data_bits(_work) = (long)_data;
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ }
+ #endif
+ //#undef PREPARE_WORK
+ typedef void (* WORK_PROC)(void *);
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+ #define PENDING_WORK_ITEM(item) (item.pending != 0)
+ #define NO_WORK_DATA(item) (!item.data)
+ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+ #define SET_WORK_ARG(__item, __data) (*work_data_bits(&(__item)) = (long)__data)
+ #define NO_WORK_DATA(item) (!(*work_data_bits(&(item))))
+ #define WORK_DATA(item) (*work_data_bits(&(item)))
+ #define PENDING_WORK_ITEM(item) (work_pending(&(item)))
+ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
+
+
+ #define SCHEDULE_DELAYED_WORK(item) schedule_delayed_work(&item, 0)
+ #define SCHEDULE_IMMEDIATE_WORK(item) SCHEDULE_WORK((item))
+
+
+ //#define _MOD_DEC_USE_COUNT //Not used in 2.6
+ //#define _MOD_INC_USE_COUNT //Not used in 2.6
+
+ //#define MODPARM(a) _##a
+
+ //#define MOD_PARM_BOOL(a, d, v) static int _##a = v; module_param_named(a, _##a, bool, 0); MODULE_PARM_DESC(a, d)
+ //#define MOD_PARM_INT(a, d, v) static int _##a = v; module_param_named(a, _##a, uint, 0); MODULE_PARM_DESC(a, d)
+ //#define MOD_PARM_STR(a, d, v) static char * _##a = v; module_param_named(a, _##a, charp, 0); MODULE_PARM_DESC(a, d)
+ #define OTG_INTERRUPT 0 /* SA_INTERRUPT in some environments */
+
+
+
+
+#else /* LINUX26 */
+
+ #define OLD_WORK_STRUCT tq_struct
+ #define OLD_WORK_ITEM tq_struct
+ typedef struct OLD_WORK_ITEM OLD_WORK_ITEM;
+ #define PREPARE_WORK_ITEM(item,work_routine,work_data) { item.routine = work_routine; item.data = work_data; }
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+ #define NO_WORK_DATA(item) (!(item).data)
+ #define PENDING_WORK_ITEM(item) (item.sync != 0)
+ //#define _MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+ //#define _MOD_INC_USE_COUNT MOD_INC_USE_COUNT
+
+ //#define MODPARM(a) a
+
+ //#define MOD_PARM_BOOL(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ //#define MOD_PARM_INT(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ //#define MOD_PARM_STR(a, d, v) static char * a = v; MOD_PARM(a, "s"); MOD_PARM_DESC(a, d);
+
+ typedef void (* WORK_PROC)(void *);
+
+ #define OTG_INTERRUPT 0 /* SA_INTERRUPT in some environments */
+#endif /* LINUX26 */
+
+/*! @} */
+
+/*! @name Linux Module support
+ */
+/*@{*/
+#if !defined(_LINUX_MODULE_H)
+ #include <linux/module.h>
+#endif /* _LINUX_MODULE_H */
+
+#if defined(MODULE)
+
+ #if defined(OTG_EXTRA_INFO)
+ #warning MODULE DEFINED
+ #endif
+
+ #define MOD_EXIT(exit_routine) module_exit(exit_routine)
+ #define MOD_INIT(init_routine) module_init(init_routine)
+ #define MOD_PROC(proc) (proc)
+ #define MOD_AUTHOR(string) MODULE_AUTHOR(string)
+ #define MOD_PARM(param, type) MODULE_PARM(param, type)
+ #define MOD_PARM_DESC(param,desc) MODULE_PARM_DESC(param, desc)
+ #define MOD_DESCRIPTION(description) MODULE_DESCRIPTION(description)
+ #define OTG_EXPORT_SYMBOL(symbol) EXPORT_SYMBOL(symbol)
+ #define OTG_EPILOGUE 1 /* EPILOGUE ROUTINE NEEDED */
+
+ #if defined(LINUX26)
+ #define MODPARM(a) _##a
+ #define MOD_PARM_BOOL(a, d, v) static int _##a = v; module_param_named(a, _##a, bool, 0); MODULE_PARM_DESC(a, d)
+ #define MOD_PARM_INT(a, d, v) static int _##a = v; module_param_named(a, _##a, uint, 0); MODULE_PARM_DESC(a, d)
+ #define MOD_PARM_STR(a, d, v) static char * _##a = v; module_param_named(a, _##a, charp, 0); MODULE_PARM_DESC(a, d)
+ #else /* defined(LINUX26) */
+ #define MODPARM(a) a
+ #define MOD_PARM_BOOL(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ #define MOD_PARM_INT(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ #define MOD_PARM_STR(a, d, v) static char * a = v; MOD_PARM(a, "s"); MOD_PARM_DESC(a, d);
+ #endif /* defined(LINUX26) */
+
+#else /* defined(MODULE) */
+ #if defined(OTG_EXTRA_INFO)
+ #warning "Modules are not enabled for this kernel"
+ #endif
+
+
+ #define MOD_EXIT(exit_routine)
+ #define MOD_INIT(init_routine) module_init(init_routine)
+ #define MOD_PROC(proc) NULL
+ #define MOD_AUTHOR(string)
+ #define MOD_PARM(param, type)
+ #define MOD_PARM_DESC(param,desc)
+ #define MOD_DESCRIPTION(description)
+ #define OTG_EXPORT_SYMBOL(symbol) //EXPORT_SYMBOL(symbol)
+ #undef EXPORT_SYMBOL
+ #define OTG_EPILOGUE 0 /* EPILOGUE ROUTINE NOT NEEDED */
+
+ #define MODPARM(a) a
+ #define MOD_PARM_BOOL(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ #define MOD_PARM_INT(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ #define MOD_PARM_STR(a, d, v) static char * a = v; MOD_PARM(a, "s"); MOD_PARM_DESC(a, d);
+
+#endif /* defined(MODULE) */
+
+//#undef MODULE_AUTHOR
+//#undef MODULE_DESCRIPTION
+//#undef MODULE_PARM_DESC
+//#undef MODULE_PARM
+#if defined(LINUX24) || defined(LINUX26)
+ #include <linux/version.h>
+ #if defined(MODULE) && (LINUX_VERSION_CODE >= KERNEL_VERSION (2,4,17))
+ //"GPL License applies under certain cirumstances; consult your vendor for details"
+ #define EMBED_LICENSE() MODULE_LICENSE ("GPL")
+ #else
+ #define EMBED_LICENSE() //Operation not supported for earlier Linux kernels
+ #endif
+
+#else /* defined(LINUX24) || defined(LINUX26) */
+ #error "Need to define EMBED_LICENSE for the current operating system"
+#endif /* defined(LINUX24) || defined(LINUX26) */
+#define EMBED_MODULE_INFO(section,moduleinfo) static char __##section##_module_info[] = moduleinfo "tt/root@belcarra.com/debian286.bbb"
+#define EMBED_USBD_INFO(moduleinfo) EMBED_MODULE_INFO(usbd,moduleinfo)
+#define GET_MODULE_INFO(section) __##section##_module_info
+
+/*! @} */
+
+/* ********************************************************************************************* */
+/* Linux specific - not part of otg-os.h implementation
+ */
+
+
+#if defined(LINUX26)
+/*! @name OTG Bus Type
+ */
+/*@{*/
+extern struct bus_type otg_bus_type;
+/*@}*/
+#endif /* defined(LINUX26) */
+
+
+
+#if defined(LINUX26)
+ #include <linux/gfp.h>
+#define GET_KERNEL_PAGE() __get_free_page(GFP_KERNEL)
+
+#else /* LINUX26 */
+
+ #include <linux/mm.h>
+ #define GET_KERNEL_PAGE() get_free_page(GFP_KERNEL)
+ #if !defined(IRQ_HANDLED)
+ // Irq's
+ typedef void irqreturn_t;
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #define IRQ_RETVAL(x)
+ #endif
+#endif /* LINUX26 */
+
+
+#define LINUX_VERSION(a,b,c) (LINUX_VERSION_CODE >= KERNEL_VERSION(a,b,c))
+
+static otg_tick_t inline otg_do_div(otg_tick_t a, otg_tick_t b)
+{
+ // XXX au1x00 has u64 ticks for example
+
+ #ifdef TICKS_IS_U64
+
+ #if defined(LINUX26)
+ return do_div(a, b);
+ #else /* defined(LINUX26) */
+ return (a / b);
+ #endif /* defined(LINUX26) */
+
+ #else /* TICKS_IS_U64 */
+ return (a / b);
+ #endif /* TICKS_IS_U64 */
+}
+
+
+
+#endif /* _OTG_LINUX_H */
diff --git a/drivers/otg/otg/otg-list.h b/drivers/otg/otg/otg-list.h
new file mode 100644
index 000000000000..3c4cc6f627af
--- /dev/null
+++ b/drivers/otg/otg/otg-list.h
@@ -0,0 +1,111 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-list.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-list.h|20061218212925|37335
+ *
+ * Copyright (c) 2004 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+#ifndef _OTG_LIST_H
+#define _OTG_LIST_H 1
+
+/*
+ *
+ * This file defines doubly linked list capabilities
+ * needed. Many operating systems have built in capabilities
+ * which match and may possibly be borrowed.
+ * In that case this file will not be needed.
+ *
+ * The implementation here is for demonstration purposes.
+ * Notes: the operations here may require protection from
+ * interruption. This is OS-dependent and not shown here.
+*/
+
+/* A list node structure is the anchor of a list
+ * A pointer to a node is used to identify items in the list
+ * A node structure is embedded in the items
+ */
+/*! @struct otg_list_node otg-list.h "otg/otg-list.h"
++ */
+struct otg_list_node {
+ struct otg_list_node *previous, *next;
+};
+typedef struct otg_list_node otg_list_node_t, *otg_list_node_ptr;
+//#define LIST_NODE struct list_head
+
+//A macro to define an list node within a structure
+#define OTG_LIST_NODE struct otg_list_node
+#define OTG_LIST_NODE_INIT(name) struct otg_list_node name = {&name, &name};
+
+/* Each item in the list is a C structure. Each item is a CONTAINER. Within
+ * each container is a member of type OTG_LIST_NODE. A CONTAINER is also
+ * called an ENTRY
+ */
+
+/* Assume that an OTG_LIST_NODE pointer is embedded as structure
+ * Recover numerical offset of member "member" within a pointer to a "type"
+ */
+#define __OTG_PTR_OFFSET(type, member) (u32) ( &(((type *) 0)->member) )
+
+/* Recover typed pointer from pointer to interior of type at the given offset
+ */
+#define __OTG_PTR_CONTAINER(ptr, type, member) (type *) ((char *) ptr - __OTG_PTR_OFFSET(type,member))
+
+/* From a pointer to the embedded NODE within an ENTRY, calculate a pointer to the
+ * ENTRY itself, properly typecast
+ */
+#define OTG_LIST_ENTRY(pointer, type, member) __OTG_PTR_CONTAINER(pointer, type, member)
+
+/* Recover list header embedded in structure which is a member of a list
+ */
+#define OTG_LIST_MEMBER(pointer, member) &(ptr->member)
+
+
+/* Add an entry at the logical end of the list
+ */
+#define OTG_LIST_ADD_TAIL(new_entry, list_head) __otg_list_add_tail(new_entry, list_head)
+static INLINE void __otg_list_add_tail(struct otg_list_node *new_entry, struct otg_list_node *list_head)
+{
+ otg_list_node_t *old_previous = list_head->previous;
+ old_previous->next = new_entry;
+ new_entry->next = list_head;
+ new_entry->previous = old_previous;
+ list_head -> previous = new_entry;
+
+}
+
+/* Delete an entry from the surrounding list
+ */
+#define OTG_LIST_DEL_ENTRY(del_entry) __otg_list_del_entry(&(del_entry))
+
+static void INLINE __otg_list_del_entry(struct otg_list_node *del_entry){
+ //Remove from list
+ del_entry->previous->next = del_entry->next;
+ del_entry->next->previous = del_entry->previous;
+ //Invalidate the entry
+ del_entry->previous = NULL;
+ del_entry->next = NULL;
+}
+
+#define OTG_LIST_FOR_EACH(cursor, list) for(cursor=(list)->next; cursor != (list); cursor=cursor->next)
+
+#define LIST_NODE_INIT(name) OTG_LIST_NODE_INIT(name)
+#define LIST_ENTRY(pointer,type,member) OTG_LIST_ENTRY(pointer,type,member)
+#define LIST_ADD_TAIL(new_entry, list_head) OTG_LIST_ADD_TAIL(new_entry, list_head)
+#define LIST_DEL_ENTRY(del_entry) OTG_LIST_DEL_ENTRY(del_entry)
+#define LIST_DEL(del_entry) OTG_LIST_DEL_ENTRY(del_entry)
+#define LIST_FOR_EACH(cursor, list) OTG_LIST_FOR_EACH(cursor, list)
+
+#endif /*_OTG_LIST_H */
diff --git a/drivers/otg/otg/otg-module.h b/drivers/otg/otg/otg-module.h
new file mode 100644
index 000000000000..cdac7221e4a1
--- /dev/null
+++ b/drivers/otg/otg/otg-module.h
@@ -0,0 +1,32 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-module.h
+ * @(#) sl@belcarra.com|otg/otg/otg-module.h|20060725042911|22922
+ *
+ * Copyright (c) 2004 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+/*!
+ * @file otg/otg/otg-module.h
+ * @brief Linux Module OS Compatibility defines
+ *
+ *
+ * @ingroup OTGCore
+ */
+#ifndef _OTG_MODULE_H
+#define _OTG_MODULE_H 1
+
+
+#endif
diff --git a/drivers/otg/otg/otg-ocd.h b/drivers/otg/otg/otg-ocd.h
new file mode 100644
index 000000000000..d3b9ba27c35e
--- /dev/null
+++ b/drivers/otg/otg/otg-ocd.h
@@ -0,0 +1,110 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg-ocd.h - OTG Controller Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-ocd.h|20061218212925|27282
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+/*!
+ * @file otg/otg/otg-ocd.h
+ * @brief Defines common to On-The-Go OTG Controller Support
+ *
+ * This file defines the ocd_ops and ocd_instance structures.
+ *
+ * The ocd_ops structure contains all of the output functions that will
+ * be called as required by the OTG event handler when changing states.
+ *
+ * The ocd_instance structure is used to maintain the global data
+ * required by the OTG controller drivers.
+ *
+ * @ingroup OTGAPI
+ * @ingroup OCD
+ */
+
+/*!
+ * @name OCD OTG Controller Driver
+ * @{
+ */
+/*! @struct ocd_instance otg-ocd.h "otg/otg-ocd.h"
+ */
+
+struct ocd_instance {
+ struct otg_instance *otg;
+ otg_tag_t TAG;
+ void * privdata;
+};
+
+
+typedef int (*otg_timer_callback_proc_t) (void *);
+
+#define OCD_CAPABILITIES_DR 1 << 0
+#define OCD_CAPABILITIES_PO 1 << 1
+#define OCD_CAPABILITIES_TR 1 << 2
+#define OCD_CAPABILITIES_HOST 1 << 3
+
+#define OCD_CAPABILITIES_AUTO 1 << 4
+
+
+/*!
+ * @struct ocd_ops
+ * The ocd_ops structure contains pointers to all of the functions implemented for the
+ * linked in driver. Having them in this structure allows us to easily determine what
+ * functions are available without resorting to ugly compile time macros or ifdefs
+ *
+ * There is only one instance of this, defined in the device specific lower layer.
+ */
+struct ocd_ops {
+
+ u32 capabilities; /* OCD Capabilities */
+
+ /* Driver Initialization - by degrees
+ */
+ int (*mod_init) (struct otg_instance *); /*!< OCD Module Initialization */
+ void (*mod_exit) (struct otg_instance *); /*!< OCD Module Exit */
+
+ otg_output_proc_t ocd_init_func; /*!< OTG calls to initialize or de-initialize the OCD */
+
+ int (*start_timer) (struct otg_instance *, int);/*!< called by OTG to start timer */
+ otg_tick_t (*ticks) (void); /*!< called by OTG to fetch current ticks, typically micro-seconds when available */
+ otg_tick_t (*elapsed) ( otg_tick_t *, otg_tick_t *);
+ /*!< called by OTG to get micro-seconds elapsed between two ticks */
+ //u32 interrupts; /*!< called by OTG to get number of interrupts */
+ //
+
+ void *privdata;
+};
+
+
+#if 0
+struct ocd_instance {
+ struct otg_instance *otg;
+ void * privdata;
+};
+#endif
+
+#ifndef OTG_APPLICATION
+
+#define REMOVE_OCD ocd_trace_tag
+extern otg_tag_t REMOVE_OCD;
+extern struct ocd_instance *REMOVE_ocd_instance;
+extern struct ocd_ops ocd_ops;
+#endif
+/* @} */
diff --git a/drivers/otg/otg/otg-os.h b/drivers/otg/otg/otg-os.h
new file mode 100644
index 000000000000..42ccf6395d1d
--- /dev/null
+++ b/drivers/otg/otg/otg-os.h
@@ -0,0 +1,329 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-os.h - USB Device Bus Interface Driver Interface
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-os.h|20061101065829|45204
+ *
+ * Copyright (c) 2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+
+/*!
+ * @name OTGCORE OTG OS Definitions
+ * This contains the OTG OS structures and definitions.
+ * @{
+ *
+ * This file should be included after an os specific library implementation
+ * to ensure that the API is correctly implemneted.
+ *
+ * OTG Task
+ * Long lived, low priority, high latency tasks.
+ *
+ * OTG WorkItem
+ * Short lived, low priority, high latency task.
+ *
+ * OTG Tasklet
+ * Short lived, high priority, low latency tasklets.
+ *
+ */
+
+/*!
+ * @name Compilation Support
+ *
+ * @{
+ *
+ * Macros to support packed structures
+ * #define PACKED1 __attribute__((packed))
+ * #define PACKED2
+ * #define PACKED0
+ *
+ * Macros to support packed enum's
+ * #define PACKED_ENUM enum
+ * #define PACKED_ENUM_EXTRA
+ *
+ * #define INLINE __inline__
+ *
+ * @ingroup OSAPI
+ *
+ */
+
+
+/* @} */
+
+/*!
+ * @name Semaphore
+ *
+ * Posix compatible Semaphores.
+ *
+ * @{
+ *
+ * typedef sem_t otg_sem_t;
+ *
+ */
+
+extern int otg_sem_init(char *, otg_sem_t *sem, int pshared, unsigned value);
+
+extern int otg_sem_init_locked(char *, otg_sem_t *sem);
+extern int otg_sem_init_unlocked(char *, otg_sem_t *sem);
+
+extern int otg_sem_destroy(otg_sem_t *sem);
+extern int otg_sem_wait(otg_sem_t *sem);
+extern int otg_sem_trywait(otg_sem_t *sem);
+extern int otg_sem_post(otg_sem_t *sem);
+
+/*! @} */
+
+
+/*!
+ * @name Mutex
+ *
+ * Posix compatible Mutexes.
+ *
+ * @{
+ *
+ * typedef pthread_mutex_t otg_pthread_mutex_t;
+ *
+ */
+
+//extern int otg_pthread_mutex_init(otg_pthread_mutex_t *mutex, const phtread_mutexattr_t *attr);
+//extern int otg_pthread_mutex_destroy(otg_pthread_mutex_t *mutex);
+
+extern int otg_pthread_mutex_lock(otg_pthread_mutex_t *mutex);
+extern int otg_pthread_mutex_trylock(otg_pthread_mutex_t *mutex);
+extern int otg_pthread_mutex_unlock(otg_pthread_mutex_t *mutex);
+
+
+/*! @} */
+
+/*!
+ * @name Atomic
+ *
+ * Basic Atomic operations.
+ * @{
+ *
+ * typedef int otg_atomic_t;
+ *
+ * };
+ */
+
+extern void otg_atomic_set(otg_atomic_t *a, int b);
+extern void otg_atomic_add(int a, otg_atomic_t *b);
+extern void otg_atomic_sub(int a, otg_atomic_t *b);
+extern void otg_atomic_clr(otg_atomic_t *a);
+extern void otg_atomic_inc(otg_atomic_t *a);
+extern void otg_atomic_dec(otg_atomic_t *a);
+extern int otg_atomic_read(otg_atomic_t *a);
+
+/*! @} */
+
+/*!
+ * @name Memory Allocation
+ *
+ * Simple allocated memory operations.
+ * @{
+ */
+
+extern void * otg_cmalloc(int n);
+extern void otg_free(void *);
+
+
+/*! @} */
+
+/*!
+ * @name Task Allocation
+ *
+ * Long lived, low priority, high latency tasks.
+ *
+ * Tasks may use semaphores, mutexes, atomic operations and memory allocations.
+ *
+ * @{
+ *
+ * TASKS are long lived, MAY HAVE low priority and high latency, the typical
+ * implementation has the work item wait in a semaphore waiting for work. :w
+ *
+ * The TASK work functions are structured to run until terminated, using
+ * a semaphore to wait for work.
+ *
+ * TASK creation:
+ * struct otg_task *task = otg_task_init("taskname", task_work, TAG);
+ * if (task)
+ * otg_task_start(task);
+ *
+ * TASK termination:
+ * otg_task_exit(task);
+ *
+ * TASK work notification
+ * otg_up_work(task);
+ *
+ * TASK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_task *task = (struct otg_task *)data;
+ * void *mydata = task->data;
+ * otg_up_admin(task);
+ * do {
+ * // wait for work
+ * otg_down_work(task);
+ * // do work
+ * } while (!task->terminating);
+ * task->terminated = TRUE;
+ * otg_up_admin(task);
+ * }
+ *
+ * static struct otg_task *otg_task_init(char *name, void (*work) (void *), void *data, otg_tag_t tag)
+ * static void otg_up_work(struct otg_task *task)
+ * static void otg_up_admin(struct otg_task *task)
+ * static void otg_down_work(struct otg_task *task)
+ * static void otg_down_admin(struct otg_task *task)
+ * static void otg_task_start(struct otg_task *task)
+ * static void otg_task_exit(struct otg_task *task)
+ *
+ */
+
+extern struct otg_task *otg_task_init2(char *name, otg_task_proc_t work, otg_task_arg_t data, otg_tag_t tag);
+extern void otg_up_work(struct otg_task *task);
+extern void otg_up_admin(struct otg_task *task);
+extern void otg_down_work(struct otg_task *task);
+extern void otg_down_admin(struct otg_task *task);
+extern void otg_task_start(struct otg_task *task);
+extern void otg_task_exit(struct otg_task *task);
+
+extern void otg_sleep(int n);
+
+
+/*! @} */
+
+/*!
+ * @name Work Allocation
+ *
+ * Short lived, low priority, high latency task.
+ *
+ * @{
+ *
+ * Work items are similar to tasks except that they are not expected
+ * to run for long periods of time or wait.
+ *
+ * The Work item function is run once and exit when the work
+ * is finished.
+ *
+ * static struct otg_workitem *otg_workitem_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_workitem_start(struct otg_workitem *tasklet)
+ * static void otg_workitem_exit(struct otg_workitem *tasklet)
+ *
+ * WORK creation:
+ * struct otg_workitem *tasklet = otg_workitem_init("taskletname", tasklet_work, TAG);
+ *
+ * WORK start:
+ * otg_workitem_start(tasklet);
+ *
+ * WORK termination:
+ * otg_workitem_exit(tasklet);
+ *
+ * WORK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_workitem *tasklet = (struct otg_workitem *)data;
+ * void *mydata = task->data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+
+extern struct otg_workitem *otg_workitem_init(char *name, otg_workitem_proc_t proc, otg_workitem_arg_t data, otg_tag_t tag);;
+extern void otg_workitem_start(struct otg_workitem *work);
+extern void otg_workitem_exit(struct otg_workitem *work);
+
+
+/*! @} */
+
+/*!
+ * @name Tasklet Allocation
+ *
+ * Short lived, high priority, low latency tasklets.
+ *
+ * Tasklets MAY NOT use memory allocation or wait on semaphores.
+ *
+ * Taskslets may use signal semaphores, mutexes and atomic operations.
+ * @{
+ *
+ * TASKLETS are short lived, MUST run with low latency and SHOULD have high
+ * priority.
+ *
+ * The TASKLET work function is structure to run once and exit when the work
+ * is finished.
+ *
+ * static struct otg_tasklet *otg_tasklet_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_tasklet_start(struct otg_tasklet *tasklet)
+ * static void otg_tasklet_exit(struct otg_tasklet *tasklet)
+ *
+ * TASKLET creation:
+ * struct otg_tasklet *tasklet = otg_tasklet_init("taskletname", tasklet_work, TAG);
+ *
+ * TASKLET start:
+ * otg_tasklet_start(tasklet);
+ *
+ * TASKLET termination:
+ * otg_tasklet_exit(tasklet);
+ *
+ * TASKLET work process
+ *
+ * void task_work(unsigned long data)
+ * {
+ * struct otg_tasklet *tasklet = (struct otg_tasklet *)data;
+ * void *mydata = task->data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+
+extern struct otg_tasklet *otg_tasklet_init(char *name, otg_tasklet_proc_t proc, otg_tasklet_arg_t data, otg_tag_t tag);
+extern void otg_tasklet_start(struct otg_tasklet *tasklet);
+extern void otg_tasklet_exit(struct otg_tasklet *tasklet);
+
+
+/*! @} */
+
+/*!
+ * @name DMA Cache
+ *
+ * @{
+ *
+ *
+ * #define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+ * #define CACHE_SYNC_TX(buf, len) consistent_sync (NULL, buf, len, PCI_DMA_TODEVICE)
+ */
+/*! @} */
+
+
+/*!
+ * @name GCC Optimize
+ *
+ * @{
+ *
+ *
+ * otg_likely()
+ * otg_unlikely()
+ *
+ */
+/*! @} */
diff --git a/drivers/otg/otg/otg-pcd.h b/drivers/otg/otg/otg-pcd.h
new file mode 100644
index 000000000000..0fbfc36a6c69
--- /dev/null
+++ b/drivers/otg/otg/otg-pcd.h
@@ -0,0 +1,120 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg-pcd.h - OTG Peripheral Controller Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-pcd.h|20070819221238|02897
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/otg/otg-pcd.h
+ * @brief Defines common to On-The-Go Peripheral Controller Support
+ *
+ * This file defines the pcd_ops and pcd_instance structures.
+ *
+ * The pcd_ops structure contains all of the output functions that will
+ * be called as required by the OTG event handler when changing states.
+ *
+ * The pcd_instance structure is used to maintain the global data
+ * required by the peripheral controller drivers.
+ *
+ * @ingroup OTGAPI
+ * @ingroup PCD
+ */
+
+/*!
+ * @name PCD Peripheral Controller Driver
+ * @{
+ */
+
+/*!
+ * @struct pcd_ops otg-pcd.h "otg/otg-pcd.h"
+ *
+ * The pcd_ops structure contains pointers to all of the functions implemented for the
+ * linked in driver. Having them in this structure allows us to easily determine what
+ * functions are available without resorting to ugly compile time macros or ifdefs
+ *
+ * There is only one instance of this, defined in the device specific lower layer.
+ */
+struct pcd_ops {
+
+ int (*mod_init) (struct otg_instance *); /*!< PCD Module Initialization */
+ void (*mod_exit) (struct otg_instance *); /*!< PCD Module Exit */
+
+ otg_output_proc_t pcd_init_func; /*!< OTG calls to initialize or de-initialize the PCD */
+ otg_output_proc_t pcd_en_func; /*!< OTG calls to enable or disable the PCD */
+ otg_output_proc_t remote_wakeup_func; /*!< OTG calls to have PCD perform remote wakeup */
+
+ framenum_t framenum; /*!< OTG calls to get current USB Frame number */
+
+ otg_output_proc_t tcd_en_func; /*!< OTG calls to enable or disable the TCD */
+ otg_output_proc_t dp_pullup_func; /*!< OTG calls to enable or disable D+ pullup (aka loc_conn) */
+ otg_tick_t (*ticks) (void); /*!< called by OTG to get ticks, typically micro-seconds when available */
+ otg_tick_t (*elapsed) ( otg_tick_t *, otg_tick_t *);
+
+
+ u8 max_endpoints;
+ //u32 ep_in_mask;
+ //u32 ep_out_mask;
+};
+
+#define REMOVE_PCD pcd_trace_tag
+extern otg_tag_t REMOVE_PCD;
+extern struct pcd_instance *REMOVE_pcd_instance;
+extern struct pcd_ops pcd_ops;
+
+/*!
+ * @struct pcd_instance otg-pcd.h "otg/otg-pcd.h"
+ */
+struct pcd_instance {
+ struct otg_instance *otg; /*!< pointer to OTG Instance */
+ otg_tag_t TAG;
+ struct usbd_bus_instance *bus; /*!< pointer to usb bus instance */
+ void * privdata; /*!< pointer to private data for PCD */
+ int pcd_exiting; /*!< non-zero if OTG is unloading */
+ //struct WORK_STRUCT bh; /*!< work structure for bottom half handler */
+ struct otg_workitem *register_bh; /*!< work structure for bottom half handler */
+ struct otg_workitem *deregister_bh; /*!< work structure for bottom half handler */
+ struct otg_task *task;
+ otg_pthread_mutex_t mutex;
+ int active;
+ u8 address;
+ u8 new_address;
+};
+
+
+#if !defined(OTG_C99)
+extern void pcd_global_init(void);
+#endif /* !defined(OTG_C99) */
+extern void pcd_init_func(struct otg_instance *, u8 );
+extern void pcd_en_func(struct otg_instance *, u8 );
+extern void pcd_remote_wakeup(struct otg_instance *, u8 );
+extern void pcd_tcd_en_func(struct otg_instance *, u8 );
+extern void pcd_dp_pullup_func(struct otg_instance *, u8 );
+extern u16 pcd_framenum(struct otg_instance *);
+extern otg_tick_t pcd_ticks(void);
+extern otg_tick_t pcd_elapsed(otg_tick_t *, otg_tick_t *);
+
+extern int pcd_request_endpoints(struct pcd_instance *, struct usbd_endpoint_map *, int, struct usbd_endpoint_request *);
+
+extern struct pcd_ops pcd_ops;
+
+
+/* @} */
diff --git a/drivers/otg/otg/otg-pci.h b/drivers/otg/otg/otg-pci.h
new file mode 100644
index 000000000000..ee2be345b319
--- /dev/null
+++ b/drivers/otg/otg/otg-pci.h
@@ -0,0 +1,282 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-pci.h -- Generic PCI driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-pci.h|20061218212925|55364
+ *
+ * Copyright (c) 2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/otg-pci.h
+ * @brief Generic PCI Driver.
+ *
+ * This is the base PCI driver for supporting the PCI devices. It is
+ * setup to allow the various OTG drivers to register and be handled
+ * separately.
+ *
+ * Multiple devices are supported.
+ *
+ *
+ * TCD PCD HCD OCD
+ * | | | |
+ * -------------------------
+ * device PCI
+ * -------------------------
+ * otg PCI
+ * -------------------------
+ * OS PCI
+ * -------------------------
+ * hardware
+ *
+ *
+ * It will probe the hardware and support registration for the sub-device
+ * drivers (ocd, hcd and pcd).
+ *
+ * otg_pci_register_driver() and otg_pci_unregister_driver() are used
+ * to register the base PCI hardware driver information. Which should
+ * result in the hardware getting probed.
+ *
+ * otg_register_driver() and otg_unregister_driver() are used to register
+ * the ocd, pcd, hcd, tcd drivers.
+ *
+ * The Device PCI driver should ensure that the basic hardware and interrupt
+ * setup is performed during it's probe. Then call otg_pci_prove().
+ *
+ */
+
+#define OTG_DRIVER_TCD 0
+#define OTG_DRIVER_PCD 1
+#define OTG_DRIVER_HCD 2
+#define OTG_DRIVER_OCD 3
+#define OTG_DRIVER_TYPES 4
+
+
+struct otg_pci;
+
+
+/*! @struct otg_pci_driver otg-pci.h "otg/otp-pci.h"
+ *
+ * @brief base pci hardware driver struct providing interface
+ * to OS pci
+ */
+struct otg_pci_driver {
+ char *name;
+ int id;
+
+
+ irqreturn_t (*isr)(struct otg_pci *, void *);
+
+ int (*probe)(struct otg_pci *dev, int id);
+ void (*remove)(struct otg_pci *dev, int id);
+
+#ifdef CONFIG_PM
+ void (*suspend)(struct otg_pci *dev, u32 state, int id); /* Device suspended */
+ void (*resume)(struct otg_pci *dev, int id); /* Device woken up */
+#endif /* CONFIG_PM */
+
+};
+
+/*! otg_pci_device_driver
+ */
+struct otg_pci_device_driver {
+
+ int pci_regions;
+ char *name;
+ irqreturn_t (*isr)(struct otg_pci *, void *);
+
+ /* sub-drivers supporting various OTG functions from the hardware */
+ struct otg_pci_driver *drivers[OTG_DRIVER_TYPES];
+};
+
+
+struct otg_pci {
+ /* base hardware PCI driver and interrupt handler */
+ struct pci_dev *pci_dev;
+
+ struct otg_pci_device_driver *otg_pci_device_driver;
+
+ struct otg_pci *next;
+
+ spinlock_t lock;
+
+
+ /* PCI mapping information */
+ //unsigned long resource_start;
+ //unsigned long resource_len;
+ //void *base;
+
+ void *regs[DEVICE_COUNT_RESOURCE];
+
+ int pci_regions;
+
+ int id;
+
+ /* OTG Driver data */
+ void *drvdata;
+
+ otg_tag_t PCI;
+
+ char procfs[32];
+ u32 chiprev;
+
+ struct otg_instance *otg_instance;
+
+ //struct ocd_instance *ocd_instance;
+ //struct pcd_instance *pcd_instance;
+ //struct tcd_instance *tcd_instance;
+ //struct hcd_instance *hcd_instance;
+
+ u32 interrupts;
+};
+
+
+
+int __devinit otg_pci_probe (struct pci_dev *pci_dev, const struct pci_device_id *id, struct otg_pci_device_driver *otg_pci_device_driver);
+void __devexit otg_pci_remove (struct pci_dev *pci_dev);
+
+extern int otg_pci_register_driver(struct otg_pci_device_driver*, struct otg_pci_driver *);
+extern void otg_pci_unregister_driver(struct otg_pci_device_driver*, struct otg_pci_driver *);
+
+extern void otg_pci_set_drvdata(struct otg_pci *, void *);
+extern void * otg_pci_get_drvdata(struct otg_pci *);
+
+
+/* ********************************************************************************************** */
+
+static u32 inline otg_readl(struct otg_pci *dev, int region, u32 reg)
+{
+ return readl(dev->regs[region] + reg);
+}
+
+static u32 inline otg_readlv(struct otg_pci *dev, int region, u32 reg, char *msg)
+{
+ u32 data = otg_readl(dev, region, reg);
+ TRACE_MSG4(dev->PCI, "[%08x:%04x] %08x %s", dev->regs[region], reg, data, msg);
+ return data;
+}
+
+static void inline otg_writel(struct otg_pci *dev, int region, u32 data, u32 reg)
+{
+ writel(data, dev->regs[region] + reg);
+}
+
+static void inline otg_writelv(struct otg_pci *dev, int region, u32 data, u32 reg, char *msg)
+{
+ otg_writel(dev, region, data, reg);
+ TRACE_MSG4(dev->PCI, "[%08x:%04x] %08x %s", dev->regs[region], reg, data, msg);
+}
+
+
+static u16 inline otg_readw(struct otg_pci *dev, int region, u32 reg)
+{
+ u16 data = readw(dev->regs[region] + reg);
+ TRACE_MSG2(dev->PCI, "[%04x] %04x", reg, data);
+ //printk(KERN_INFO"%s: [%04x] %04x\n", __FUNCTION__, reg, data);
+ return data;
+}
+
+static void inline otg_writew(struct otg_pci *dev, int region, u16 data, u32 reg)
+{
+ TRACE_MSG2(dev->PCI, "[%04x] %04x", reg, data);
+ //printk(KERN_INFO"%s: [%04x] %04x\n", __FUNCTION__, reg, data);
+ writew(data, dev->regs[region] + reg);
+}
+
+static u8 inline otg_readb(struct otg_pci *dev, int region, u32 reg)
+{
+ u8 data = readb(dev->regs[region] + reg);
+ TRACE_MSG2(dev->PCI, "[%04x] %02x", reg, data);
+ //printk(KERN_INFO"%s: [%04x] %02x\n", __FUNCTION__, reg, data);
+ return data;
+}
+
+static void inline otg_writeb(struct otg_pci *dev, int region, u8 data, u32 reg)
+{
+ TRACE_MSG2(dev->PCI, "[%04x] %02x", reg, data);
+ //printk(KERN_INFO"%s: [%04x] %02x\n", __FUNCTION__, reg, data);
+ writeb(data, dev->regs[region] + reg);
+}
+
+/* ********************************************************************************************* */
+
+static u32 inline otg_readl0(struct otg_pci *dev, u32 reg)
+{
+ return otg_readl(dev, 0, reg);
+}
+static void inline otg_writel0(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writel(dev, 0, data, reg);
+}
+
+static u32 inline otg_readl1(struct otg_pci *dev, u32 reg)
+{
+ return otg_readl(dev, 1, reg);
+}
+static void inline otg_writel1(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writel(dev, 1, data, reg);
+}
+
+static u32 inline otg_readl2(struct otg_pci *dev, u32 reg)
+{
+ return otg_readl(dev, 2, reg);
+}
+static void inline otg_writel2(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writel(dev, 2, data, reg);
+}
+
+static u32 inline otg_readl3(struct otg_pci *dev, u32 reg)
+{
+ return otg_readl(dev, 3, reg);
+}
+static void inline otg_writel3(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writel(dev, 3, data, reg);
+}
+
+static u32 inline otg_readl3v(struct otg_pci *dev, u32 reg)
+{
+ return otg_readlv(dev, 3, reg, "");
+}
+
+static u32 inline otg_readl3m(struct otg_pci *dev, u32 reg, char *msg)
+{
+ return otg_readlv(dev, 3, reg, msg);
+}
+
+static void inline otg_writel3m(struct otg_pci *dev, u32 data, u32 reg, char *msg)
+{
+ otg_writelv(dev, 3, data, reg, msg);
+}
+
+static void inline otg_writel3v(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writelv(dev, 3, data, reg, "");
+}
+
+static u32 inline otg_readl4(struct otg_pci *dev, u32 reg)
+{
+ return otg_readl(dev, 4, reg);
+}
+static void inline otg_writel4(struct otg_pci *dev, u32 data, u32 reg)
+{
+ otg_writel(dev, 4, data, reg);
+}
+
+/* End of FILE */
diff --git a/drivers/otg/otg/otg-task.h b/drivers/otg/otg/otg-task.h
new file mode 100644
index 000000000000..dcec7d50287f
--- /dev/null
+++ b/drivers/otg/otg/otg-task.h
@@ -0,0 +1,338 @@
+/*
+ * 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
+ */
+xxxxx
+xxxx
+#if 0
+/*
+ * otg/otg/otg-task.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-task.h|20061017072623|50255
+ *
+ * Copyright (c) 2006 Belcarra Technologies 2005 Corp
+ *
+ */
+
+/*!
+ * @file otg/otg/otg-linux.h
+ * @brief Linux OS Compatibility defines
+ *
+ * @ingroup OTGCore
+ */
+#ifndef _OTG_TASK_H
+#define _OTG_TASK_H 1
+
+#if !defined(_OTG_MODULE_H)
+#include <otg/otg-module.h>
+#endif
+
+
+/*! @name OTG Task API
+ *
+ */
+
+/*@{*/
+/*
+ */
+
+
+/* XXX OTG_TASK_WORK
+ *
+ * Linux Implementation Configuration
+ *
+ * #define OTG_TASK_WORK
+ * Use work items only for TASK implemenation
+ * #define OTG_TASKLET_WORK
+ * Use work items only for TASK implemenation
+ *
+ * N.B. the work item implementation for tasklets is not suitable
+ * for full OTG Dual-Role implemenation.
+ *
+ */
+
+#define OTG_TASK_WORK
+//#undef OTG_TASK_WORK
+
+//#define OTG_TASKLET_WORK
+#undef OTG_TASKLET_WORK
+
+
+
+/*! struct otg_task
+ */
+typedef void *otg_task_arg_t;
+typedef void (* otg_task_proc_t) (otg_task_arg_t data);
+struct otg_task {
+ #if !defined(OTG_TASK_WORK)
+ struct workqueue_struct *work_queue;
+ struct semaphore admin_sem;
+ struct semaphore work_sem;
+ #endif /* defined(OTG_TASK_WORK) */
+ struct work_struct work;
+ BOOL terminate;
+ BOOL terminated;
+ otg_task_arg_t *data;
+ BOOL debug;
+ otg_tag_t tag;
+ char *name;
+};
+
+
+/*! otg_task_init
+ * Create otg task structure, create workqueue, initialize it.
+ */
+static inline struct otg_task *otg_task_init(char *name, otg_task_proc_t work, otg_task_arg_t data, otg_tag_t tag)
+{
+ struct otg_task *task;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((task = CKMALLOC(sizeof (struct otg_task), GFP_KERNEL)));
+
+ task->data = data;
+ task->tag = tag;
+ task->name = name;
+
+ #if defined(OTG_TASK_WORK)
+ task->terminated = task->terminate = TRUE;
+
+ #else /* defined(OTG_TASK_WORK) */
+
+ task->terminated = task->terminate = FALSE;
+ #if defined(LINUX26)
+ THROW_UNLESS((task->work_queue = create_singlethread_workqueue(name)), error);
+ #else /* LINUX26 */
+ THROW_UNLESS((task->work_queue = create_workqueue(name)), error);
+ #endif /* LINUX26 */
+ init_MUTEX_LOCKED(&task->admin_sem);
+ init_MUTEX_LOCKED(&task->work_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+
+ INIT_WORK(&task->work, work, task);
+
+ return task;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (task) LKFREE(task);
+ return NULL;
+ }
+}
+
+/*! otg_up_work
+ * Signal work task to re-start.
+ */
+static void inline otg_up_work(struct otg_task *task)
+{
+ //TRACE_STRING(task->tag, "UP WORK: %s", task->name);
+
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(OTG_TASK_WORK)
+ task->terminated = FALSE;
+ SCHEDULE_WORK(task->work);
+ #else /* defined(OTG_TASK_WORK) */
+ UP(&task->work_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_up_admin
+ * Signal start task to re-start.
+ */
+static void inline otg_up_admin(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "UP ADMIN: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ UP(&task->admin_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_down_work
+ * Used by work task to wait.
+ */
+static void inline otg_down_work(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN WORK: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ while (down_interruptible(&task->work_sem));
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_down_admin
+ * Used by admin task to wait.
+ */
+static void inline otg_down_admin(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN ADMIN: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ while (down_interruptible(&task->admin_sem));
+ //DOWN(&task->admin_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_task_start
+ * Start and wait for otg task.
+ */
+static void inline otg_task_start(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "START: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ /* schedule work item and wait until it starts */
+ queue_work(task->work_queue, &task->work);
+ otg_down_admin(task);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_task_exit
+ * Terminate and wait for otg task.
+ */
+static void inline otg_task_exit(struct otg_task *task)
+{
+ TRACE_STRING(task->tag, "EXIT: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(OTG_TASK_WORK)
+ while (!task->terminated) {
+ SCHEDULE_TIMEOUT(10 * HZ);
+ }
+ #else /* defined(OTG_TASK_WORK) */
+ /* signal termination */
+ task->terminate = TRUE;
+ otg_up_work(task);
+ otg_down_admin(task);
+
+ /* destroy workqueue */
+ flush_workqueue(task->work_queue);
+ destroy_workqueue(task->work_queue);
+ #endif /* defined(OTG_TASK_WORK) */
+
+ LKFREE(task);
+}
+
+/*! struct otg_tasklet
+ */
+#ifdef OTG_TASKLET_WORK
+typedef void *otg_tasklet_arg_t;
+#else /* OTG_TASKLET_WORK */
+typedef unsigned long otg_tasklet_arg_t;
+#endif /* OTG_TASKLET_WORK */
+typedef void (* otg_tasklet_proc_t) (otg_tasklet_arg_t data);
+struct otg_tasklet {
+ #ifdef OTG_TASKLET_WORK
+ struct work_struct work;
+ #else /* OTG_TASKLET_WORK */
+ struct tasklet_struct tasklet;
+ #endif /* OTG_TASKLET_WORK */
+ BOOL terminated;
+ otg_task_arg_t data;
+ BOOL debug;
+ otg_tag_t tag;
+ char *name;
+};
+
+
+
+/*! otg_tasklet_init
+ * Create otg task structure, create workqueue, initialize it.
+ */
+static inline struct otg_tasklet *otg_tasklet_init(char *name, otg_tasklet_proc_t work, otg_tasklet_arg_t data, otg_tag_t tag)
+{
+ struct otg_tasklet *tasklet;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((tasklet = CKMALLOC(sizeof (struct otg_tasklet), GFP_KERNEL)));
+
+ tasklet->tag = tag;
+ tasklet->name = name;
+ tasklet->terminated = TRUE;
+
+ #ifdef OTG_TASKLET_WORK
+ INIT_WORK(&tasklet->work, work, data);
+ #else /* OTG_TASKLET_WORK */
+ tasklet_init(&tasklet->tasklet, work, data);
+ #endif /* OTG_TASKLET_WORK */
+
+ return tasklet;
+
+ CATCH(error) {
+ if (tasklet) LKFREE(tasklet);
+ return NULL;
+ }
+}
+
+/*! otg_tasklet_start
+ * Signal work task to re-start.
+ */
+static void inline otg_tasklet_start(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "START: %s", name);
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ tasklet->terminated = FALSE;
+ #ifdef OTG_TASKLET_WORK
+ SCHEDULE_WORK(tasklet->work);
+ #else /* OTG_TASKLET_WORK */
+ tasklet_schedule(&tasklet->tasklet);
+ #endif /* OTG_TASKLET_WORK */
+}
+
+/*! otg_tasklet_exit
+ * Terminate and wait for otg task.
+ */
+static void inline otg_tasklet_exit(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "EXIT: %s", name);
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ #ifdef OTG_TASKLET_WORK
+ while (!tasklet->terminated) {
+ printk(KERN_INFO"%s: SLEEPING\n", __FUNCTION__);
+ SCHEDULE_TIMEOUT(10 * HZ);
+ printk(KERN_INFO"%s: RUNNING\n", __FUNCTION__);
+ }
+ #else /* OTG_TASKLET_WORK */
+ tasklet_kill(&tasklet->tasklet);
+ #endif /* OTG_TASKLET_WORK */
+
+ LKFREE(tasklet);
+}
+
+
+/*! @} */
+
+#endif /* _OTG_TASK_H */
+#endif
diff --git a/drivers/otg/otg/otg-tcd.h b/drivers/otg/otg/otg-tcd.h
new file mode 100644
index 000000000000..8864a34175fe
--- /dev/null
+++ b/drivers/otg/otg/otg-tcd.h
@@ -0,0 +1,143 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg-tcd.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-tcd.h|20061218212925|42044
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @addgroup OTGTCD Transceiver Controller Driver Support
+ * @ingroup OTGOCD
+ */
+/*!
+ * @file otg/otg/otg-tcd.h
+ * @brief Defines common to On-The-Go Transceiver Controller Support
+ *
+ * This file defines the tcd_ops and tcd_instance structures.
+ *
+ * The tcd_ops structure contains all of the output functions that will
+ * be called as required by the OTG event handler when changing states.
+ *
+ * The tcd_instance structure is used to maintain the global data
+ * required by the transceiver controller drivers.
+ *
+ * @ingroup OTGAPI
+ * @ingroup TCD
+ */
+
+/*!
+ * @name TCD Transceiver Controller Driver
+ * @{
+ */
+
+
+/*!
+ * @struct tcd_ops otg-tcd.h "otg/otg-tcd.h"
+ * The pcd_ops structure contains pointers to all of the functions implemented for the
+ * linked in driver. Having them in this structure allows us to easily determine what
+ * functions are available without resorting to ugly compile time macros or ifdefs
+ *
+ * There is only one instance of this, defined in the device specific lower layer.
+ */
+struct tcd_ops {
+ /* Driver Initialization - by degrees
+ */
+ int (*mod_init) (struct otg_instance *); /*!< TCD Module Initialization */
+ void (*mod_exit) (struct otg_instance *); /*!< TCD Module Exit */
+
+ otg_current_t initial_state;
+
+ void * privdata;
+
+
+ /* 3. called for usbd_vbus() if defined */
+ //int (*vbus) (struct otg_instance *); /* return non-zero if the Vbus valid */
+ //int (*id) (struct otg_instance *); /* return non-zero if the ID valid */
+
+ /* 4. called by otg event to control outputs
+ */
+
+ otg_output_proc_t tcd_init_func; /*!< OTG calls to initialize or de-initialize the HCD */
+ otg_output_proc_t tcd_en_func; /*!< OTG calls to enable or disable the TCD */
+ otg_output_proc_t chrg_vbus_func; /*!< OTG calls to enable or disable charging Vbus */
+ otg_output_proc_t drv_vbus_func; /*!< OTG calls to enable or disable Vbus */
+ otg_output_proc_t dischrg_vbus_func; /*!< OTG calls to enable or disable discharging Vbus */
+ otg_output_proc_t dp_pullup_func; /*!< OTG calls to enable or disable D+ pullup (aka loc_conn) */
+ otg_output_proc_t dm_pullup_func; /*!< OTG calls to enable or disable D- pullup (aka loc_carkit) */
+ otg_output_proc_t dp_pulldown_func; /*!< OTG calls to enable or disable D+ pulldown (aka loc_conn) */
+ otg_output_proc_t dm_pulldown_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */
+ otg_output_proc_t peripheral_host_func; /*!< OTG calls to enable or disable D- pulldown (aka loc_carkit) */
+ otg_output_proc_t overcurrent_func; /*!< OTG calls to clear overcurrent indication */
+ otg_output_proc_t dm_det_func; /*!< OTG calls to enable or disable D+ pullup detection */
+ otg_output_proc_t dp_det_func; /*!< OTG calls to enable or disable D- pullup detection */
+ otg_output_proc_t cr_det_func; /*!< OTG calls to enable or disable D+ CRINT detection */
+ otg_output_proc_t charge_pump_func; /*!< OTG calls to enable or disable external charge pump */
+ otg_output_proc_t bdis_acon_func; /*!< OTG calls to enable or disable the BDIS ACON feature */
+ otg_output_proc_t mx21_vbus_drain_func; /*!< OTG calls to enable or disable the mx21 vbus drain feature */
+ otg_output_proc_t id_pulldown_func; /*!< OTG calls to enable or disable ID pulldown to ground */
+ otg_output_proc_t audio_func; /*!< OTG calls to enable or disable audio function */
+ otg_output_proc_t uart_func; /*!< OTG calls to enable or disable uart function */
+ otg_output_proc_t mono_func; /*!< OTG calls to enable or disable mono function */
+};
+
+/*!
+ * @struct hcd_instance otg-tcd.h "otg/otg-tcd.h"
+ */
+struct tcd_instance {
+ struct otg_instance *otg; /*!< pointer to OTG Instance */
+ otg_tag_t TAG;
+
+ BOOL vbus;
+ BOOL id;
+
+ void *privdata;
+
+ BOOL inputs_valid;
+ otg_current_t inputs;
+};
+
+
+//extern struct trace_ops trace_ops;
+extern struct tcd_ops tcd_ops;
+
+
+// XXX The following will be REMOVED in the near future
+// XXX If you need to use them, at REMOVE_ to your code
+// XXX to allow use. But plan on removing, use the otg->
+// XXX equivalents.
+// XXX
+extern struct tcd_instance *REMOVE_tcd_instance;
+#define REMOVE_TCD tcd_trace_tag
+extern otg_tag_t REMOVE_TCD;
+
+
+extern int tcd_vbus (struct otg_instance *otg);
+
+/* pcd_cable_event[_irq] - these can be called in a traditional PCD implementation
+ * to set b_sess_vld appropriately when vbus is sensed (pcd must implement ocd_ops.vbus)
+ */
+extern void tcd_cable_event_irq (struct otg_instance *otg, u8 connect);
+
+extern void tcd_cable_event (struct otg_instance *otg, u8 connect);
+
+//extern void tcd_init(struct otg_instance *, u8 );
+
+/* @} */
diff --git a/drivers/otg/otg/otg-trace.h b/drivers/otg/otg/otg-trace.h
new file mode 100644
index 000000000000..61305e5534e8
--- /dev/null
+++ b/drivers/otg/otg/otg-trace.h
@@ -0,0 +1,415 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-trace.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-trace.h|20061218212938|61040
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/otg-trace.h
+ * @brief Core Defines for USB OTG Core Layaer
+ *
+ * Fast Trace Utility
+ * This set of definitions and code is meant to provide a _fast_ debugging facility
+ * (much faster than printk) so that time critical code can be debugged by looking
+ * at a trace of events provided by reading a file in the procfs without affecting
+ * the timing of events in the critical code.
+ *
+ * The mechanism used it to allocate a (large) ring buffer of relatively small structures
+ * that include location and high-res timestamp info, and up to 8 bytes of optional
+ * data. Values are stored and timestamps are taken as the critical code runs, but
+ * data formatting and display are done during the procfs read, when more time is
+ * available :).
+ *
+ * Note that there is usually some machine dependent code involved in getting the
+ * high-res timestamp, and there may be other bits used just to keep the overall
+ * time impact as low as possible.
+ *
+ * Varargs up to 9 arguments are now supported, but you have to supply the number
+ * of args, since examining the format string for the number at trace event time
+ * was deemed too expensive time-wise.
+ *
+ *
+ * @ingroup OTGCore
+ */
+
+#ifndef OTG_TRACE_H
+#define OTG_TRACE_H 1
+
+/*!
+ * @var typedef struct otg_tag otg_tag_t
+ */
+typedef struct otg_tag {
+ void *otg;
+ //char msg[16];
+ char *msg;
+ u8 tag;
+} *otg_tag_t;
+
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif /* PRAGMAPACK */
+
+/*!create a type for otg_trace_types */
+typedef PACKED_ENUM /*enum*/ otg_trace_types {
+ otg_trace_msg_invalid_n,
+ otg_trace_msg_va_start_n,
+ otg_trace_send_n,
+ otg_trace_recv_n,
+ otg_trace_nsend_n,
+ otg_trace_nrecv_n,
+ otg_trace_setup_n,
+ otg_trace_string_n,
+ otg_trace_elapsed_n,
+
+} PACKED_ENUM_EXTRA otg_trace_types_t;
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif /* PRAGMAPACK */
+
+/*! @name otg_trace_message otg trace message type defines */
+
+/*!@{ */
+
+#define OTG_TRACE_MAX_IN_VA 8
+
+
+/*! @typedef struct trace otg_trace_t
+ * @brief define an alias for struct trace
+ *
+ */
+#define OTG_TRACE_IN_INTERRUPT (1 << 0)
+#define OTG_TRACE_ID_GND (1 << 1)
+#define OTG_TRACE_HCD_PCD (1 << 2)
+
+#if !defined(OTG_TRACE_DISABLE) && ( defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE))
+
+#if defined(CONFIG_OTG_TRACE_PACKED)
+#define TRACE_PACKED0 PACKED0
+#define TRACE_PACKED1 PACKED1
+#define TRACE_PACKED2 PACKED2
+#else
+#define TRACE_PACKED0
+#define TRACE_PACKED1
+#define TRACE_PACKED2
+#endif
+
+/*! @typedef struct trace otg_trace_t
+ * @brief define an alias for struct trace
+ *
+ */
+typedef TRACE_PACKED0 struct TRACE_PACKED1 trace {
+ otg_trace_types_t otg_trace_type;
+ u8 tag;
+ u8 va_num_args;
+ u8 flags;
+
+ const char *function;
+ u32 interrupts;
+ otg_tick_t ticks;
+ u16 h_framenum;
+ u16 p_framenum;
+
+ char *fmt;
+
+ union {
+ u8 dump[32];
+ u8 setup[8];
+ u8 string[32];
+ u32 val[OTG_TRACE_MAX_IN_VA];
+ struct {
+ u32 id;
+ otg_tick_t ticks;
+ };
+
+ } trace; /*< share space for different message */
+
+} TRACE_PACKED2 otg_trace_t;
+
+#else
+//#warning TRACE DISABLED
+/*! create a type a trace */
+typedef struct trace {
+ int a;
+} otg_trace_t;
+#endif
+
+/*!
+ * create an alias for otg_trace_snapshot
+ */
+
+typedef struct otg_trace_snapshot {
+ int first;
+ int next;
+ int total;
+ otg_trace_t *traces;
+} otg_trace_snapshot;
+
+/* @} */
+
+ #define TRACE_MAX 0x00008000
+ #define TRACE_MASK 0x00007FFF
+//#define TRACE_MAX 0x0000800
+//#define TRACE_MASK 0x00007FF
+//#define TRACE_MAX 0x000080
+//#define TRACE_MASK 0x00007F
+
+
+#if !defined(OTG_TRACE_DISABLE) && ( defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE))
+
+/*Don't produce unrelated TRACE MSG */
+
+
+#ifdef XXXCONFIG_OTG_LATENCY_CHECK
+
+#if !defined(OLD_TRACE_API)
+
+extern void otg_trace_dump(otg_tag_t tag, const char *fn, otg_trace_types_t trace_type, int len, void *dump);
+extern void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup);
+extern void otg_trace_string(otg_tag_t tag, const char *fn, char *fmt, void *setup);
+extern void otg_trace_elapsed(otg_tag_t tag, const char *fn, char *fmt, u32 id, otg_tick_t ticks);
+
+#define TRACE_RECV(tag,len,dump)
+#define TRACE_SEND(tag,len,dump)
+#define TRACE_NRECV(tag,len,dump)
+#define TRACE_NSEND(tag,len,dump)
+#define TRACE_SETUP(tag,setup)
+#define TRACE_STRING(tag,fmt,setup)
+#define TRACE_ELAPSED(tag,fmt,ticks,id) otg_trace_elapsed(tag,__FUNCTION__,fmt,id, ticks)
+#define TRACE_MSG0(tag, msg)
+
+#else
+
+extern int otg_trace_first;
+extern int otg_trace_last_read;
+extern int otg_trace_next;
+
+
+#endif
+
+extern void otg_trace_msg(
+ otg_tag_t tag,
+ const char *fn,
+ u8 nargs,
+ char *fmt,
+ u32 a1, u32 a2, u32 a3, u32 a4, u32 a5, u32 a6, u32 a7, u32 a8);
+
+#define TRACE_MSG1(tag, fmt, a1)
+#define TRACE_MSG2(tag, fmt, a1, a2)
+#define TRACE_MSG3(tag, fmt, a1, a2, a3)
+#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4)
+#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5)
+#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6)
+#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7)
+#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8)
+
+/*Latency TRACE*/
+
+#define LTRACE_MSG0(tag, msg) otg_trace_msg(tag, __FUNCTION__, 0, msg, 0, 0, 0, 0, 0, 0, 0, 0)
+
+#define LTRACE_MSG1(tag, fmt, a1) \
+ otg_trace_msg(tag, __FUNCTION__, 1, fmt, (u32)(a1), 0, 0, 0, 0, 0, 0, 0)
+
+#define LTRACE_MSG2(tag, fmt, a1, a2) \
+ otg_trace_msg(tag, __FUNCTION__, 2, fmt, (u32)(a1), (u32)(a2), 0, 0, 0, 0, 0, 0)
+
+#define LTRACE_MSG3(tag, fmt, a1, a2, a3) \
+ otg_trace_msg(tag, __FUNCTION__, 3, fmt, (u32)(a1), (u32)(a2), (u32)(a3), 0, 0, 0, 0, 0)
+
+
+#define LTRACE_MSG4(tag, fmt, a1, a2, a3, a4) \
+ otg_trace_msg(tag, __FUNCTION__, 4, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), 0, 0, 0, 0)
+
+
+#define LTRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \
+ otg_trace_msg(tag, __FUNCTION__, 5, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), (u32)(a5), 0, 0, 0)
+
+#define LTRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \
+ otg_trace_msg(tag, __FUNCTION__, 6, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), (u32)(a5), (u32)(a6), 0, 0)
+
+#define LTRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \
+ otg_trace_msg(tag, __FUNCTION__, 7, fmt, (u32)(a1), (u32)(a2), (u32)(a3),\
+ (u32)(a4), (u32)(a5), (u32)(a6), (u32)(a7), 0)
+
+#define LTRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \
+ otg_trace_msg(tag, __FUNCTION__, 8, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), \
+ (u32)(a5), (u32)(a6), (u32)(a7), (u32)a8 )
+
+extern otg_tag_t otg_trace_obtain_tag(void *, char *);
+extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag);
+
+
+#else
+
+#if !defined(OLD_TRACE_API)
+
+extern void otg_trace_dump(otg_tag_t tag, const char *fn, otg_trace_types_t trace_type, int len, void *dump);
+extern void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup);
+extern void otg_trace_string(otg_tag_t tag, const char *fn, char *fmt, void *setup);
+extern void otg_trace_elapsed(otg_tag_t tag, const char *fn, char *fmt, u32 id, otg_tick_t ticks);
+
+#define TRACE_RECV(tag,len,dump) otg_trace_dump(tag,__FUNCTION__,otg_trace_recv_n,len,dump)
+#define TRACE_SEND(tag,len,dump) otg_trace_dump(tag,__FUNCTION__,otg_trace_send_n,len,dump)
+#define TRACE_NRECV(tag,len,dump) otg_trace_dump(tag,__FUNCTION__,otg_trace_nrecv_n,len,dump)
+#define TRACE_NSEND(tag,len,dump) otg_trace_dump(tag,__FUNCTION__,otg_trace_nsend_n,len,dump)
+#define TRACE_SETUP(tag,setup) otg_trace_setup(tag,__FUNCTION__,setup)
+#define TRACE_STRING(tag,fmt,setup) otg_trace_string(tag,__FUNCTION__,fmt,setup)
+#define TRACE_ELAPSED(tag,fmt,ticks,id) otg_trace_elapsed(tag,__FUNCTION__,fmt,id, ticks)
+#define TRACE_MSG0(tag, msg) otg_trace_msg(tag, __FUNCTION__, 0, msg, 0, 0, 0, 0, 0, 0, 0, 0)
+
+#else
+
+extern int otg_trace_first;
+extern int otg_trace_last_read;
+extern int otg_trace_next;
+
+
+#endif
+
+extern void otg_trace_msg(
+ otg_tag_t tag,
+ const char *fn,
+ u8 nargs,
+ char *fmt,
+ u32 a1, u32 a2, u32 a3, u32 a4, u32 a5, u32 a6, u32 a7, u32 a8);
+
+#define TRACE_MSG1(tag, fmt, a1) \
+ otg_trace_msg(tag, __FUNCTION__, 1, fmt, (u32)(a1), 0, 0, 0, 0, 0, 0, 0)
+
+#define TRACE_MSG2(tag, fmt, a1, a2) \
+ otg_trace_msg(tag, __FUNCTION__, 2, fmt, (u32)(a1), (u32)(a2), 0, 0, 0, 0, 0, 0)
+
+#define TRACE_MSG3(tag, fmt, a1, a2, a3) \
+ otg_trace_msg(tag, __FUNCTION__, 3, fmt, (u32)(a1), (u32)(a2), (u32)(a3), 0, 0, 0, 0, 0)
+
+
+#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \
+ otg_trace_msg(tag, __FUNCTION__, 4, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), 0, 0, 0, 0)
+
+
+#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \
+ otg_trace_msg(tag, __FUNCTION__, 5, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), (u32)(a5), 0, 0, 0)
+
+#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \
+ otg_trace_msg(tag, __FUNCTION__, 6, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), (u32)(a5), (u32)(a6), 0, 0)
+
+#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \
+ otg_trace_msg(tag, __FUNCTION__, 7, fmt, (u32)(a1), (u32)(a2), (u32)(a3),\
+ (u32)(a4), (u32)(a5), (u32)(a6), (u32)(a7), 0)
+
+#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \
+ otg_trace_msg(tag, __FUNCTION__, 8, fmt, (u32)(a1), (u32)(a2), (u32)(a3), (u32)(a4), \
+ (u32)(a5), (u32)(a6), (u32)(a7), (u32)a8 )
+
+#define LTRACE_MSG1(tag, fmt, a1)
+#define LTRACE_MSG2(tag, fmt, a1, a2)
+#define LTRACE_MSG3(tag, fmt, a1, a2, a3)
+#define LTRACE_MSG4(tag, fmt, a1, a2, a3, a4)
+#define LTRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5)
+#define LTRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6)
+#define LTRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7)
+#define LTRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8)
+
+extern otg_tag_t otg_trace_obtain_tag(void *, char *);
+extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag);
+
+#endif
+
+#elif defined(OTG_WINCE)
+
+#define TRACE_MSG0(tag, msg) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s"), _T(msg)))
+
+#define TRACE_MSG1(tag, fmt, a1) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d"), _T(fmt), a1))
+
+#define TRACE_MSG2(tag, fmt, a1, a2) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2))
+
+#define TRACE_MSG3(tag, fmt, a1, a2, a3) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d"), _T(fmt), a1, a2, a3));
+
+#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d"), _T(fmt), a1, a2, a3, a4));
+
+#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5));
+
+#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6));
+
+#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7));
+
+#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \
+ DEBUGMSG(ZONE_INIT,(_T("OTGCORE - CORE - %s %d %d %d %d %d %d %d %d %d"), _T(fmt), a1, a2, a3, a4, a5, a6, a7, a8));
+
+extern otg_tag_t otg_trace_obtain_tag(void *, char *);
+extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag);
+
+
+extern otg_tag_t otg_trace_obtain_tag(void *, char *);
+extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag);
+
+#define TRACE_TMP
+
+#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+
+#define TRACE_RECV(tag,len,dump)
+#define TRACE_SEND(tag,len,dump)
+#define TRACE_NRECV(tag,len,dump)
+#define TRACE_NSEND(tag,len,dump)
+#define TRACE_SETUP(tag,setup)
+#define TRACE_STRING(tag,fmt,setup)
+#define TRACE_ELAPSED(tag,fmt,id,ticks)
+
+#define TRACE_MSG0(tag, fmt) \
+ while (0) { int x; x = (int) (tag); }
+#define TRACE_MSG1(tag, fmt, a1) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); }
+#define TRACE_MSG2(tag, fmt, a1, a2) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); }
+#define TRACE_MSG3(tag, fmt, a1, a2, a3) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); }
+#define TRACE_MSG4(tag, fmt, a1, a2, a3, a4) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); x = (int)(a4); }
+#define TRACE_MSG5(tag, fmt, a1, a2, a3, a4, a5) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); x = (int)(a4); \
+ x = (int)(a5); }
+#define TRACE_MSG6(tag, fmt, a1, a2, a3, a4, a5, a6) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); x = (int)(a4); \
+ x = (int)(a5); x = (int)(a6); }
+#define TRACE_MSG7(tag, fmt, a1, a2, a3, a4, a5, a6, a7) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); x = (int)(a4); \
+ x = (int)(a5); x = (int)(a6); x = (int)(a7); }
+#define TRACE_MSG8(tag, fmt, a1, a2, a3, a4, a5, a6, a7, a8) \
+ while (0) { int x; x = (int) (tag); x = (int)(a1); x = (int)(a2); x = (int)(a3); x = (int)(a4); \
+ x = (int)(a5); x = (int)(a6); x = (int)(a7); x = (int)(a8); }
+
+extern otg_tag_t otg_trace_obtain_tag(void *, char *);
+extern otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag);
+
+#define TRACE_TMP //
+
+#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+
+
+#endif /* OTG_TRACE_H */
diff --git a/drivers/otg/otg/otg-utils.h b/drivers/otg/otg/otg-utils.h
new file mode 100644
index 000000000000..916bdca027a1
--- /dev/null
+++ b/drivers/otg/otg/otg-utils.h
@@ -0,0 +1,161 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/otg-utils.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/otg-utils.h|20070612174152|35048
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * Basic utilities for OTG package
+ */
+/*!
+ * @file otg/otg/otg-utils.h
+ * @brief Useful macros.
+ *
+ *
+ * @ingroup OTGCore
+ */
+
+
+#if !defined(_OTG_UTILS_H)
+#define _OTG_UTILS_H 1
+
+/*! @name Min, Max and zero
+ * @{
+ */
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#define ZERO(a) memset(&a, 0, sizeof(a))
+
+/* @} */
+
+/*! @name TRUE and FALSE
+ *
+ * @{
+ */
+#if !defined(TRUE)
+#define TRUE 1
+#endif
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+
+#define BOOL int
+#define BOOLEAN(e)((e)?TRUE:FALSE)
+
+/* @} */
+
+
+/*! @name CATCH and THROW
+ *
+ * These macros implement a conceptually clean way to use
+ * C's goto to implement a standard error handler. The typical forms are:
+ *
+ * THROW(error); // continue execution in CATCH statement
+ * THROW_IF(test, error); // continue execution in CATCH statement if test true
+ * THROW_UNLESS(test, error); // continue execution in CATCH statement if test false
+ *
+ * // program execution will continue after the CATCH statement;
+ * CATCH(error) {
+ * // handle error here
+ * }
+ *
+ * @{
+ */
+#define CATCH(x) while(0) x:
+#define THROW(x) goto x
+#define THROW_IF(e, x) if (e) goto x;
+#define THROW_UNLESS(e, x) UNLESS (e) goto x;
+/* @} */
+
+
+/*! @name UNLESS
+ *
+ * This implements a simple equivalent to negated if (expression).
+ *
+ * Unless expression then statement else statement.
+ *
+ * @{
+ */
+#define unless(x) if(!(x))
+#define UNLESS(x) if(!(x))
+/* @} */
+
+/*! @name BREAK_ and CONTINUE_
+ *
+ * These implement a conditional break and continue statement.
+ *
+ * @{
+ */
+#define BREAK_UNLESS(x) UNLESS (x) break;
+#define BREAK_IF(x) if (x) break;
+#define CONTINUE_IF(x) if (x) continue;
+#define CONTINUE_UNLESS(x) UNLESS (x) continue;
+
+/* @} */
+
+
+/*! @name RETURN_
+ *
+ * These implement conditional return statement.
+ * @{
+ */
+#define RETURN_IF(x) if (x) return;
+#define RETURN_ZERO_IF(x) if (x) return 0;
+#define RETURN_NULL_IF(x) if (x) return NULL;
+#define RETURN_EBUSY_IF(x) if (x) return -EBUSY;
+#define RETURN_EFAULT_IF(x) if (x) return -EFAULT;
+#define RETURN_EINVAL_IF(x) if (x) return -EINVAL;
+#define RETURN_ENOMEM_IF(x) if (x) return -ENOMEM;
+#define RETURN_ENODEV_IF(x) if (x) return -ENODEV;
+#define RETURN_EAGAIN_IF(x) if (x) return -EAGAIN;
+#define RETURN_ETIMEDOUT_IF(x) if (x) return -ETIMEDOUT;
+#define RETURN_EPIPE_IF(x) if (x) return -EPIPE;
+#define RETURN_EUNATCH_IF(x) if (x) return -EUNATCH;
+#define RETURN_IRQ_HANDLED_IF(x) if (x) return IRQ_HANDLED;
+#define RETURN_IRQ_HANDLED_IF_IRQ_HANDLED(x) if (IRQ_HANDLED == x) return IRQ_HANDLED;
+#define RETURN_IRQ_NONE_IF(x) if (x) return IRQ_NONE;
+#define RETURN_TRUE_IF(x) if (x) return TRUE;
+#define RETURN_FALSE_IF(x) if (x) return FALSE;
+
+#define RETURN_UNLESS(x) UNLESS (x) return;
+#define RETURN_NULL_UNLESS(x) UNLESS (x) return NULL;
+#define RETURN_ZERO_UNLESS(x) UNLESS (x) return 0;
+#define RETURN_EBUSY_UNLESS(x) UNLESS (x) return -EBUSY;
+#define RETURN_EFAULT_UNLESS(x) UNLESS (x) return -EFAULT;
+#define RETURN_EINVAL_UNLESS(x) UNLESS (x) return -EINVAL;
+#define RETURN_ENOMEM_UNLESS(x) UNLESS (x) return -ENOMEM;
+#define RETURN_ENODEV_UNLESS(x) UNLESS (x) return -ENODEV;
+#define RETURN_EAGAIN_UNLESS(x) UNLESS (x) return -EAGAIN;
+#define RETURN_ETIMEDOUT_UNLESS(x) UNLESS (x) return -ETIMEDOUT;
+#define RETURN_EPIPE_UNLESS(x) UNLESS (x) return -EPIPE;
+#define RETURN_EUNATCH_UNLESS(x) UNLESS (x) return -EUNATCH;
+#define RETURN_IRQ_HANDLED_UNLESS(x) UNLESS (x) return IRQ_HANDLED;
+#define RETURN_IRQ_NONE_UNLESS(x) UNLESS (x) return IRQ_NONE;
+#define RETURN_TRUE_UNLESS(x) UNLESS (x) return TRUE;
+#define RETURN_FALSE_UNLESS(x) UNLESS (x) return FALSE;
+
+#define UNTIL(x) while (!(x))
+
+/* @} */
+
+
+
+#endif /* _OTG_UTILS_H */
diff --git a/drivers/otg/otg/pcd-include.h b/drivers/otg/otg/pcd-include.h
new file mode 100644
index 000000000000..797302d3bdfa
--- /dev/null
+++ b/drivers/otg/otg/pcd-include.h
@@ -0,0 +1,49 @@
+/*
+ * 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
+ */
+/*
+ * otg/udc.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/pcd-include.h|20061017072623|59852
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/pcd-include.h
+ * @brief Linux OS Precompiled Headers
+ *
+ * @ingroup OTGCore
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+//#include <asm/irq.h>
+//#include <asm/system.h>
+//#include <asm/io.h>
+
+#include <otg/usbp-chap9.h>
+//#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+//#include <otg/otg-task.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-ocd.h>
+#include <otg/usbp-pcd.h>
diff --git a/drivers/otg/otg/usbp-audio.h b/drivers/otg/otg/usbp-audio.h
new file mode 100644
index 000000000000..e2b815e28156
--- /dev/null
+++ b/drivers/otg/otg/usbp-audio.h
@@ -0,0 +1,357 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-audio.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-audio.h|20061218212925|42686
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*
+ * @file otg/otg/usbp-audio.h
+ * @brief Audio Class class descriptor structure definitions.
+ *
+ *
+ * @ingroup USBDAPI
+ */
+
+/*!
+ * @name Audio
+ */
+ /*! @{ */
+
+#define CS_AUDIO_UNDEFINED 0x20
+#define CS_AUDIO_DEVICE 0x21
+#define CS_AUDIO_CONFIGURATION 0x22
+#define CS_AUDIO_STRING 0x23
+#define CS_AUDIO_INTERFACE 0x24
+#define CS_AUDIO_ENDPOINT 0x25
+
+#define AUDIO_HEADER 0x01
+#define AUDIO_INPUT_TERMINAL 0x02
+#define AUDIO_OUTPUT_TERMINAL 0x03
+#define AUDIO_MIXER_UNIT 0x04
+#define AUDIO_SELECTOR_UNIT 0x05
+#define AUDIO_FEATURE_UNIT 0x06
+#define AUDIO_PROCESSING_UNIT 0x07
+#define AUDIO_EXTENSION_UNIT 0x08
+/*! @} */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+
+/*! @name c.f. CDC 5.2.3 Table 24
+ * c.f. Audio Appendix A.4
+ */
+ /*! @{ */
+#define CS_UNDEFINED 0x20
+#define CS_DEVICE 0x21
+#define CS_CONFIG 0x22
+#define CS_STRING 0x23
+#define CS_INTERFACE 0x24
+#define CS_ENDPOINT 0x25
+/*! @} */
+
+/*!
+ * @name Audio Interface Class - c.f Appendix A.1
+ */
+ /*! @{ */
+#define AUDIO_INTERFACE_CLASS 0x01
+/*! @} */
+
+/*! @name Audio Interface Subclass - c.f Appendix A.2
+ */
+ /*! @{ */
+#define AUDIO_SUBCLASS_UNDEFINED 0x00
+#define AUDIO_AUDIOCONTROL 0x01
+#define AUDIO_AUDIOSTREAMING 0x02
+#define AUDIO_MIDISTREAMING 0x03
+/*! @} */
+
+/*! @name Audio Interface Proctol - c.f. Appendix A.3
+ */
+ /*! @{ */
+#define AUDIO_PR_PROTOCOL_UNDEFINED 0x00
+/*! @} */
+
+/*! @name Audio Class-Specific AC Interface Descriptor Subtypes - c.f. A.5
+ */
+ /*! @{ */
+#define AUDIO_AC_DESCRIPTOR_UNDEFINED 0x00
+#define AUDIO_AC_HEADER 0x01
+#define AUDIO_AC_INPUT_TERMINAL 0x02
+#define AUDIO_AC_OUTPUT_TERMINAL 0x03
+#define AUDIO_AC_MIXER_UNIT 0x04
+#define AUDIO_AC_SELECTOR_UNIT 0x05
+#define AUDIO_AC_FEATURE_UNIT 0x06
+#define AUDIO_AC_PROCESSING_UNIT 0x07
+#define AUDIO_AC_EXTENSION_UNIT 0x08
+/*! @} */
+
+/*! @name Audio Class-Specific AS Interface Descriptor Subtypes - c.f. A.6
+ */
+ /*! @{ */
+#define AUDIO_AS_DESCRIPTOR_UNDEFINED 0x00
+#define AUDIO_AS_GENERAL 0x01
+#define AUDIO_AS_FORMAT_TYPE 0x02
+#define AUDIO_AS_FORMAT_UNSPECFIC 0x03
+/*! @} */
+
+/*! @name Audio Class Processing Unit Processing Types - c.f. A.7
+ */
+ /*! @{ */
+#define AUDIO_PROCESS_UNDEFINED 0x00
+#define AUDIO_UP_DOWN_MUIX_PROCESS 0x01
+#define AUDIO_DOLBY_PROLOGIC_PROCESS 0x02
+#define AUDIO_3D_STEREO_EXTENDER_PROCESS 0x03
+#define AUDIO_REVERBERATION_PROCESS 0x04
+#define AUDIO_CHORUS_PROCESS 0x05
+#define AUDIO_DYN_RANGE_COMP_PROCESS 0x06
+/*! @} */
+
+/*! @name Audio Class-Specific Endpoint Descriptor Subtypes - c.f. A.8
+ */
+ /*! @{ */
+#define AUDIO_DESCRIPTOR_UNDEFINED 0x00
+#define AUDIO_EP_GENERAL 0x01
+/*! @} */
+
+/*! @name Audio Class-Specific Request Codes - c.f. A.9
+ */
+ /*! @{ */
+#define AUDIO_REQUEST_CODE_UNDEFINED 0x00
+#define AUDIO_SET_CUR 0x01
+#define AUDIO_GET_CUR 0x81
+#define AUDIO_SET_MIN 0x02
+#define AUDIO_GET_MIN 0x82
+#define AUDIO_SET_MAX 0x03
+#define AUDIO_GET_MAX 0x83
+#define AUDIO_SET_RES 0x04
+#define AUDIO_GET_RES 0x84
+#define AUDIO_SET_MEM 0x05
+#define AUDIO_GET_MEM 0x85
+#define AUDIO_GET_STAT 0xff
+/*! @} */
+
+/*!
+ * @name Audio Status Word Format - c.f. Table 3.1
+ */
+ /*! @{ */
+
+#define AUDIO_STATUS_INTERRUPT_PENDING 1<<7
+#define AUDIO_STATUS_MEMORY_CONTENT_CHANGED 1<<6
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_CONTROL_INTERFACE 0
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_INTERFACE 1
+#define AUDIO_STATUS_ORIGINATOR_AUDIO_STREAMING_ENDPOINT 2
+#define AUDIO_STATUS_ORIGINATOR_AUDIOCONTROL_INTERFACE 0
+/*! @struct usbd_audio_status_word usbp-audio.h "otg/usbp-audio.h"
+ * @brief audio status wrapper
+ */
+struct usbd_audio_status_word {
+ u8 bStatusType;
+ u8 bOriginator;
+};
+/*! @struct usbd_audio_ac_interface_header_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio AC sub interface header descriptor wrapper
+ */
+struct usbd_audio_ac_interface_header_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u16 bcdADC;
+ u16 wTotalLength;
+ u8 binCollection;
+ u8 bainterfaceNr[1];
+};
+/*! @struct usbd_audio_input_terminal_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio input terminal descriptor wrapper
+ */
+struct usbd_audio_input_terminal_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bTerminalID;
+ u16 wTerminalType;
+ u8 bAssocTerminal;
+ u8 bNrChannels;
+ u16 wChannelConfig;
+ u8 iChannelNames;
+ u8 iTerminal;
+};
+/*! @struct usbd_audio_input_terminal_description usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio input terminal description wrapper
+ */
+struct usbd_audio_input_terminal_description {
+ struct usbd_audio_input_terminal_descriptor *audio_input_terminal_descriptor;
+ u16 wTerminalType;
+ u16 wChannelConfig;
+ char * iChannelNames;
+ char * iTerminal;
+};
+/*! @struct usbd_audio_output_terminal_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio output terminal descriptor
+ */
+struct usbd_audio_output_terminal_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bTerminalID;
+ u16 wTerminalType;
+ u8 bAssocTerminal;
+ u8 bSourceID;
+ u8 iTerminal;
+};
+/*! @struct usbd_audio_output_terminal_description usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio output terminal description wrapper
+ */
+struct usbd_audio_output_terminal_description {
+ struct usbd_audio_output_terminal_descriptor *audio_output_terminal_descriptor;
+ u16 wTerminalType;
+ char * iTerminal;
+};
+/*! @struct usbd_audio_mixer_unit_descriptor_a usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio mixer unit A descriptor
+ */
+struct usbd_audio_mixer_unit_descriptor_a {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bUniteID;
+ u8 bNrinPins;
+};
+/*! @struct usbd_audio_mixer_unit_descriptor_b usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio mixer unit B descriptor
+ */
+struct usbd_audio_mixer_unit_descriptor_b {
+ u8 baSourceID;
+ u8 bNrChannels;
+ u16 wChannelConfig;
+ u8 iChannelNames;
+ u8 bmControls;
+ u8 iMixer;
+};
+/*! @struct usbd_audio_mixer_unit_description usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio mixer unit description wrapper
+ */
+struct usbd_audio_mixer_unit_description {
+ struct usbd_audio_mixer_unit_descriptor *audio_mixer_unit_descriptor;
+ u16 wChannelConfig;
+ char * iMixer;
+};
+
+/*! @struct usbd_audio_as_general_interface_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio AS interface subclass descriptior
+ */
+struct usbd_audio_as_general_interface_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bTerminalLink;
+ u8 bDelay;
+ u16 wFormatTag;
+};
+/*! @struct usbd_audio_as_general_interface_description usbp-audio.h "otg/usbp-audio.h"
++ *
++ */
+struct usbd_audio_as_general_interface_description {
+ struct usbd_audio_as_general_interface_descriptor *audio_as_general_interface_descriptor;
+ u16 wFormatTag;
+};
+/*! @struct usbd_audio_as_format_type_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio AS interface subclass format type descriptor
+ */
+struct usbd_audio_as_format_type_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bFormatType;
+ u8 bNrChannels;
+ u8 bSubFrameSize;
+ u8 bBitResolution;
+ u8 bSamFreqType;
+ u8 iSamFreq[3];
+};
+/*! @struct usbd_audio_as_format_type_description usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio AS interface subclass format type description wrapper
+ */
+struct usbd_audio_as_format_type_description {
+ struct usbd_audio_as_format_type_descriptor *audio_as_format_type_descriptor;
+ u16 wFormatTag;
+};
+
+
+
+/*! @struct usbd_audio_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio descriptor wrapper
+ */
+struct usbd_audio_descriptor {
+ union {
+ struct usbd_audio_ac_interface_header_descriptor header;
+ struct usbd_audio_input_terminal_descriptor input;
+ struct usbd_audio_output_terminal_descriptor output;
+ } descriptor;
+};
+
+
+/*! @struct usbd_ac_endpoint_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief audio control endpoint descriptor wrapper
+ */
+struct usbd_ac_endpoint_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+ u16 wMaxPacketSize;
+ u8 bInterval;
+ u8 bRefresh;
+ u8 bSynchAddress;
+};
+
+/*! @struct usbd_as_iso_endpoint_descriptor usbp-audio.h "otg/usbp-audio.h"
+ *
+ * @brief as_iso_endpoint descriptor
+ */
+struct usbd_as_iso_endpoint_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bmAttributes;
+ u8 bLockDelayUnits;
+ u16 wLockDelay;
+};
+/*! @}*/
+
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
diff --git a/drivers/otg/otg/usbp-bus.h b/drivers/otg/otg/usbp-bus.h
new file mode 100644
index 000000000000..3d371995c69c
--- /dev/null
+++ b/drivers/otg/otg/usbp-bus.h
@@ -0,0 +1,363 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-bus.h - USB Device Bus Interface Driver Interface
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/otg/usbp-bus.h|20070810200302|45685
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/otg/usbp-bus.h
+ * @brief Bus interface Defines for USB Device Core Layaer
+ *
+ * This file contains the USB Peripheral Driver definitions.
+ *
+ * This is the interface between the bottom of the USB Core and the top of
+ * the Bus Interace Drivers and is comprised of:
+ *
+ * o public functions exported by the USB Core layer
+ *
+ * o structures and functions passed by the Function Driver to the USB
+ * Core layer
+ *
+ * USB Peripheral Controller Drivers are structured such that the upper edge
+ * implements interfaces to the USB Peripheral Core layer to provide low
+ * level USB services and the lower edge interfaces to the actual USB
+ * Hardware (typically called the USB Device Controller or UDC.)
+ *
+ * The peripheral controller layer primarily deals with endpoints.
+ * There is one control endpoint and one or more data endpoints.
+ *
+ * The control endpoint is special, with received data processed with
+ * the built in EP0 function. Which in turn will pass requests to
+ * interface functions as appropriate.
+ *
+ * Data endpoints are controlled by interface driver and have a
+ * function callback specified by the interface driver to perform
+ * whatever needs to be done when data is sent or received.
+ *
+ *
+ * @ingroup USBDAPI
+ */
+
+/*
+struct usbd_endpoint_map;
+struct usbd_endpoint_instance;
+struct usbd_bus_instance;
+struct usbd_urb;
+*/
+
+#define USBD usbd_trace_tag
+extern otg_tag_t USBD;
+
+/*!
+ * USB Bus Interface Driver structures
+ *
+ * Driver description:
+ *
+ * struct usbd_bus_operations
+ * struct usbd_bus_driver
+ *
+ */
+
+extern int usbd_maxstrings;
+
+
+/*!
+ * exported to otg
+ */
+struct usbd_ops {
+ int (*admin) (char *);
+ char * (*function_name) (int);
+};
+
+
+
+/*! @struct usbd_bus_operations usbp-bus.h "otg/usbp-bus.h"
+ *
+ * @brief Operations that the slave layer or function driver can use to interact with the bus interface driver.
+ * The send_urb() function is used by the usb-device endpoint 0 driver and
+ * function drivers to submit data to be sent.
+ *
+ * The cancel_urb() function is used by the usb-device endpoint 0 driver and
+ * function drivers to remove previously queued data to be sent.
+ *
+ * The endpoint_halted() function is used by the ep0 control function to
+ * check if an endpoint is halted.
+ *
+ * The endpoint_feature() function is used by the ep0 control function to
+ * set/reset device features on an endpoint.
+ *
+ * The device_event() function is used by the usb device core to tell the
+ * bus interface driver about various events.
+ */
+struct usbd_bus_operations {
+ //int (*xbus_serial_number) (struct usbd_bus_instance *, char *);
+
+ int (*start_endpoint_in) (struct usbd_bus_instance *, struct usbd_endpoint_instance *);
+ int (*start_endpoint_out) (struct usbd_bus_instance *, struct usbd_endpoint_instance *);
+ int (*cancel_urb_irq) (struct usbd_urb *);
+ int (*device_feature) (struct usbd_bus_instance *, int, int);
+ int (*endpoint_halted) (struct usbd_bus_instance *, int);
+ int (*halt_endpoint) (struct usbd_bus_instance *, int, int);
+ int (*set_configuration) (struct usbd_bus_instance *, int);
+ int (*set_address) (struct usbd_bus_instance *, int);
+ int (*event_handler) (struct usbd_bus_instance *, usbd_device_event_t, int);
+ int (*request_endpoints) (struct usbd_bus_instance *, struct usbd_endpoint_map *,
+ int, struct usbd_endpoint_request *);
+ int (*set_endpoints) (struct usbd_bus_instance *, int , struct usbd_endpoint_map *);
+ int (*framenum) (struct usbd_bus_instance *);
+ //u64 (*ticks) (void);
+ //u64 (*elapsed) (u64 *, u64 *);
+ //u64 (*interrupts) (void);
+};
+
+/*! @struct usbd_endpoint_instance usbp-bus.h "otg/usbp-bus.h"
+ * @brief Endpoint operation related data wrapper
+ *
+ * Per endpoint configuration data. Used to track which actual physical
+ * endpoints.
+ *
+ * The bus layer maintains an array of these to represent all physical
+ * endpoints.
+ *
+ */
+struct usbd_endpoint_instance {
+ int interface_wIndex; // which interface owns this endpoint
+ int physicalEndpoint[2]; // physical endpoint address - bus interface specific
+ u8 bEndpointAddress[2]; // logical endpoint address
+ u8 bmAttributes[2]; // endpoint type
+ u16 wMaxPacketSize[2]; // packet size for requested endpoint
+ u16 transferSize[2]; // packet size for requested endpoint
+
+ u32 feature_setting; // save set feature information
+
+ // control
+ int state; // available for use by bus interface driver
+
+
+ otg_pthread_mutex_t mutex;
+ struct urb_link rdy; // empty urbs ready to receive
+
+ union {
+ struct usbd_urb *active_urb; // active urb
+ struct usbd_urb *rcv_urb; // alias
+ struct usbd_urb *tx_urb; // alias
+ };
+
+ u32 rcv_transferSize; // maximum transfer size from function driver
+ int rcv_error; // current bulk-in has an error
+ u32 tx_transferSize; // maximum transfer size from function driver
+
+ u32 sent; // data already sent
+ u32 last; // data sent in last packet XXX do we need this
+ struct timer_list pcd_hr_timer;
+ void *privdata;
+
+ struct usbd_bus_instance *bus;
+
+ void *buffer;
+ dma_addr_t dma;
+
+};
+
+/*! @name endpoint zero states
+ * @{
+ */
+#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
+#define DATA_STATE_PENDING_XMIT 5
+#define WAIT_FOR_IN_STATUS 6
+/*! @} */
+
+/*! @struct usbd_bus_driver usbp-bus.h "otg/usbp-bus.h"
+ * @brief Bus Interface data structure
+ *
+ * Keep track of specific bus interface.
+ *
+ * This is passed to the usb-device layer when registering. It contains all
+ * required information about each real bus interface found such that the
+ * usb-device layer can create and maintain a usb-device structure.
+ *
+ * Note that bus interface registration is incumbent on finding specific
+ * actual real bus interfaces. There will be a registration for each such
+ * device found.
+ *
+ * The max_tx_endpoints and max_rx_endpoints are the maximum number of
+ * possible endpoints that this bus interface can support. The default
+ * endpoint 0 is not included in these counts.
+ *
+ */
+struct usbd_bus_driver {
+ char *name;
+ u8 max_endpoints; // maximimum number of rx enpoints
+ u8 maxpacketsize;
+ u8 high_speed_capable;
+ struct usbd_device_description *device_description;
+ struct usbd_bus_operations *bops;
+ void *privdata;
+ //u32 capabilities;
+ u8 bmAttributes;
+ u8 bMaxPower;
+ u8 ports;
+};
+
+#if 0
+/*!
+ * Bus state
+ *
+ * enabled or disabled
+ */
+typedef enum usbd_bus_state {
+ usbd_bus_state_unknown,
+ usbd_bus_state_disabled,
+ usbd_bus_state_enabled
+} usbd_bus_state_t;
+#endif
+
+/*!
+ * @name UDC Capabilities
+ * @{
+ */
+#define HAVE_CABLE_IRQ 0x0001
+#define REMOTE_WAKEUP_SUPPORTED 0x0002
+#define ROOT_HUB 0x0004
+
+#define USB_SPEED_FULL 0x0
+#define USB_SPEED_HIGH 0x1
+#define USB_SPEED_UNKNOWN 0x2
+
+/*! @} */
+
+/*! @struct usbd_bus_instance usbp-bus.h "otg/usbp-bus.h"
+ * @brief Bus Interface configuration structure
+ *
+ * This is allocated for each configured instance of a bus interface driver.
+ *
+ * It contains a pointer to the appropriate bus interface driver.
+ *
+ * The privdata pointer may be used by the bus interface driver to store private
+ * per instance state information.
+ */
+struct usbd_bus_instance {
+
+ struct otg_instance *otg;
+ struct usbd_bus_driver *driver;
+
+ int endpoints;
+ struct usbd_endpoint_instance *endpoint_array; // array of available configured endpoints
+
+ struct urb_link finished; // processed urbs
+
+ usbd_device_status_t status; // device status
+ //usbd_bus_state_t bus_state;
+ usbd_device_state_t device_state; // current USB Device state
+ usbd_device_state_t suspended_state; // previous USB Device state
+
+ struct usbd_interface_instance *ep0; // ep0 configuration
+ struct usbd_function_instance *function_instance;
+
+ u8 high_speed;
+
+ u32 device_feature_settings;// save set feature information
+ u8 bmAttributes;
+ u8 bMaxPower;
+
+ //struct WORK_STRUCT reset_bh; // runs as bottom half, equivalent to interrupt time
+ //struct WORK_STRUCT resume_bh; // runs as bottom half, equivalent to interrupt time
+ //struct WORK_STRUCT suspend_bh; // runs as bottom half, equivalent to interrupt time
+
+ struct otg_workitem *reset_bh; // runs as bottom half, equivalent to interrupt time
+ struct otg_workitem *resume_bh; // runs as bottom half, equivalent to interrupt time
+ struct otg_workitem *suspend_bh; // runs as bottom half, equivalent to interrupt time
+
+ //struct otg_task *task;
+
+ void *privdata; // private data for the bus interface
+ char *arg;
+
+ int usbd_maxstrings;
+ struct usbd_string_descriptor **usb_strings;
+ struct usbd_urb *ep0_urb;
+};
+
+
+/*! bus driver registration
+ *
+ * Called by bus interface drivers to register themselves when loaded
+ * or de-register when unloading.
+ */
+struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *, int);
+void usbd_deregister_bus (struct usbd_bus_instance *);
+
+
+/*! Enable/Disable Function
+ *
+ * Called by a bus interface driver to select and enable a specific function
+ * driver.
+ */
+int usbd_enable_function (struct usbd_bus_instance *, char *, char *);
+void usbd_disable_function (struct usbd_bus_instance *);
+
+
+/*!
+ * usbd_device_request - process a received urb
+ * @urb: pointer to an urb structure
+ *
+ * Used by a USB Bus interface driver to pass received data in a URB to the
+ * appropriate USB Function driver.
+ *
+ * This function must return 0 for success and -EINVAL if the request
+ * is to be stalled.
+ *
+ * Not that if the SETUP is Host to Device with a non-zero wLength then there
+ * *MUST* be a valid receive urb queued OR the request must be stalled.
+ */
+int usbd_device_request_irq (struct usbd_bus_instance*, struct usbd_device_request *);
+int usbd_device_request (struct usbd_bus_instance*, struct usbd_device_request *);
+
+/*!
+ * Device I/O
+ */
+void usbd_urb_finished (struct usbd_urb *, int );
+void usbd_urb_finished_irq (struct usbd_urb *, int );
+//struct usbd_urb *usbd_first_urb_detached_irq (urb_link * hd);
+//struct usbd_urb *usbd_first_urb_detached (urb_link * hd);
+void usbd_do_urb_callback (struct usbd_urb *urb, int rc);
+
+
+/*! flush endpoint
+ */
+void usbd_flush_endpoint_irq (struct usbd_endpoint_instance *);
+void usbd_flush_endpoint (struct usbd_endpoint_instance *);
+int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress);
+
+/*
+ * urb lists
+ */
+//struct usbd_urb *usbd_first_urb (urb_link * hd);
+//void usbd_unlink_urb (struct usbd_urb * urb);
+
+struct usbd_urb *usbd_first_urb_detached (struct usbd_endpoint_instance *endpoint, urb_link * hd);
+struct usbd_urb *usbd_first_finished_urb_detached (struct usbd_endpoint_instance *endpoint, urb_link * hd);
+struct usbd_urb *usbd_first_ready_urb(struct usbd_endpoint_instance *endpoint, urb_link * hd);
diff --git a/drivers/otg/otg/usbp-cdc.h b/drivers/otg/otg/usbp-cdc.h
new file mode 100644
index 000000000000..13abe33218ba
--- /dev/null
+++ b/drivers/otg/otg/usbp-cdc.h
@@ -0,0 +1,689 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-cdc.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-cdc.h|20061218212925|24522
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/otg/usbp-cdc.h
+ * @brief CDC class descriptor structure definitions.
+ *
+ * @ingroup USBDAPI
+ */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+/*!
+ * @name Class-Specific Request Codes
+ * C.f. CDC Table 46
+ */
+ /*! @{ */
+
+#define CDC_CLASS_REQUEST_SEND_ENCAPSULATED 0x00
+#define CDC_CLASS_REQUEST_GET_ENCAPSULATED 0x01
+
+#define CDC_CLASS_REQUEST_SET_COMM_FEATURE 0x02
+#define CDC_CLASS_REQUEST_GET_COMM_FEATURE 0x03
+#define CDC_CLASS_REQUEST_CLEAR_COMM_FEATURE 0x04
+
+#define CDC_CLASS_REQUEST_SET_LINE_CODING 0x20
+#define CDC_CLASS_REQUEST_GET_LINE_CODING 0x21
+
+#define CDC_CLASS_REQUEST_SET_CONTROL_LINE_STATE 0x22
+#define CDC_CLASS_REQUEST_SEND_BREAK 0x23
+/*! @} */
+
+/*!
+ * @name Notification codes
+ * c.f. CDC Table 68
+ */
+ /*! @{ */
+
+#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00
+#define CDC_NOTIFICATION_RESPONSE_AVAILABLE 0x01
+#define CDC_NOTIFICATION_AUX_JACK_HOOK_STATE 0x08
+#define CDC_NOTIFICATION_RING_DETECT 0x09
+#define CDC_NOTIFICATION_SERIAL_STATE 0x20
+#define CDC_NOTIFICATION_CALL_STATE_CHANGE 0x28
+#define CDC_NOTIFICATION_LINE_STATE_CHANGE 0x29
+#define CDC_NOTIFICATION_CONNECTION_SPEED_CHANGE 0x2a
+/*! @} */
+
+
+/*!
+ * @name ACM - Line Coding structure
+ * C.f. CDC Table 50
+ */
+ /*! @{ */
+
+/*! @struct cdc_acm_line_coding usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief acm line code wrapper
+ *
+ */
+PACKED0 struct PACKED1 cdc_acm_line_coding {
+ u32 dwDTERate;
+ u8 bCharFormat;
+ u8 bParityType;
+ u8 bDataBits;
+};
+
+ /*!@} */
+
+/*!
+ * @name ACM - Line State
+ * C.f. CDC Table 51
+ */
+ /*! @{ */
+/*! create a type for u16 */
+typedef u16 cdc_acm_bmLineState;
+#define CDC_LINESTATE_D1_RTS (1 << 1)
+#define CDC_LINESTATE_D0_DTR (1 << 0)
+/*! @} */
+
+/*!
+ * Serial State - C.f. 6.3.5
+ */
+/*! @struct acm_serial_state usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief acm serial state wrapper
+ */
+PACKED0 struct PACKED1 acm_serial_state {
+ u16 uart_state_bitmap;
+};
+
+
+/*!
+* @name ACM - UART State
+ * C.f. Table 69 - UART State Bitmap Values
+ */
+/*! @{ */
+/*! create a type for u16 */
+typedef u16 cdc_acm_bmUARTState;
+#define CDC_UARTSTATE_BOVERRUN (1 << 6)
+#define CDC_UARTSTATE_BPARITY (1 << 5)
+#define CDC_UARTSTATE_BFRAMING (1 << 4)
+#define CDC_UARTSTATE_BRINGSIGNAL (1 << 3)
+#define CDC_UARTSTATE_BBREAK (1 << 2)
+#define CDC_UARTSTATE_BTXCARRIER_DSR (1 << 1)
+#define CDC_UARTSTATE_BRXCARRIER_DCD (1 << 0)
+/*! @} */
+
+/*! @struct cdc_notification_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief USB Notification descriptor struct
+ */
+PACKED0 struct PACKED1 cdc_notification_descriptor {
+ u8 bmRequestType;
+ u8 bNotification;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ u8 data[2];
+};
+
+
+
+
+/*!
+ * @name Communications Class Types
+ *
+ * c.f. CDC USB Class Definitions for Communications Devices
+ * c.f. WMCD USB CDC Subclass Specification for Wireless Mobile Communications Devices
+ *
+ */
+/*! @{ */
+
+#define CLASS_BCD_VERSION 0x0110
+/*! @} */
+
+/*! @name c.f. CDC 4.1 Table 14 */
+
+/*! @{ */
+
+#define AUDIO_CLASS 0x01
+#define COMMUNICATIONS_DEVICE_CLASS 0x02
+/*! @} */
+
+/*! @name c.f. CDC 4.2 Table 15
+ */
+ /*! @{ */
+#define COMMUNICATIONS_INTERFACE_CLASS 0x02
+/*! @} */
+
+/*! @name c.f. CDC 4.3 Table 16
+ */
+ /*! @{ */
+#define COMMUNICATIONS_NO_SUBCLASS 0x00
+#define COMMUNICATIONS_DLCM_SUBCLASS 0x01
+#define COMMUNICATIONS_ACM_SUBCLASS 0x02
+#define COMMUNICATIONS_TCM_SUBCLASS 0x03
+#define COMMUNICATIONS_MCCM_SUBCLASS 0x04
+#define COMMUNICATIONS_CCM_SUBCLASS 0x05
+#define COMMUNICATIONS_ENCM_SUBCLASS 0x06
+#define COMMUNICATIONS_ANCM_SUBCLASS 0x07
+
+#define AUDIO_CONTROL_SUBCLASS 0x01
+#define AUDIO_STREAMING_SUBCLASS 0x02
+/*! @} */
+
+/*! @name c.f. WMCD 5.1
+ */
+ /*! @{ */
+#define COMMUNICATIONS_WHCM_SUBCLASS 0x08
+#define COMMUNICATIONS_DMM_SUBCLASS 0x09
+#define COMMUNICATIONS_MDLM_SUBCLASS 0x0a
+#define COMMUNICATIONS_OBEX_SUBCLASS 0x0b
+#define COMMUNICATIONS_EEM_SUBCLASS 0x0c
+/*! @} */
+
+/*! @name c.f. CDC 4.6 Table 18
+ */
+ /*! @{ */
+#define DATA_INTERFACE_CLASS 0x0a
+/*! @} */
+
+/*! @name c.f. CDC 4.7 Table 19
+ */
+ /*! @{ */
+#define COMMUNICATIONS_NO_PROTOCOL 0x00
+#define COMMUNICATIONS_EEM_PROTOCOL 0x07
+/*! @} */
+
+/*!
+ * @name bDescriptorSubtypes
+ *
+ * c.f. CDC 5.2.3 Table 25
+ * c.f. WMCD 5.3 Table 5.3
+ */
+ /*! @{ */
+
+#define USB_ST_HEADER 0x00
+#define USB_ST_CMF 0x01
+#define USB_ST_ACMF 0x02
+#define USB_ST_DLMF 0x03
+#define USB_ST_TRF 0x04
+#define USB_ST_TCLF 0x05
+#define USB_ST_UF 0x06
+#define USB_ST_CSF 0x07
+#define USB_ST_TOMF 0x08
+#define USB_ST_USBTF 0x09
+#define USB_ST_NCT 0x0a
+#define USB_ST_PUF 0x0b
+#define USB_ST_EUF 0x0c
+#define USB_ST_MCMF 0x0d
+#define USB_ST_CCMF 0x0e
+#define USB_ST_ENF 0x0f
+#define USB_ST_ATMNF 0x10
+
+#define USB_ST_WHCM 0x11
+#define USB_ST_MDLM 0x12
+#define USB_ST_MDLMD 0x13
+#define USB_ST_DMM 0x14
+#define USB_ST_OBEX 0x15
+#define USB_ST_CS 0x16
+#define USB_ST_CSD 0x17
+#define USB_ST_TCM 0x18
+/*! @} */
+
+/*!
+ * @name Call Management - bmCapabilities
+ *
+ * c.f. CDC 5.2.3.2 Table 27
+ * c.f. WMCD
+ */
+#define USB_CMFD_DATA_CLASS 0x02
+#define USB_CMFD_CALL_MANAGEMENT 0x01
+/*! @} */
+
+/*!
+ * @name Abstract Control Management - bmCapabilities
+ *
+ * c.f. CDC 5.2.3.2 Table 28
+ * c.f. WMCD
+ */
+#define USB_ACMFD_NETWORK 0x08
+#define USB_ACMFD_SEND_BREAK 0x04
+#define USB_ACMFD_CONFIG 0x02
+#define USB_ACMFD_COMM_FEATURE 0x01
+/*! @} */
+
+/*!
+ * @name Class Descriptor Description Structures...
+ * c.f. CDC 5.1
+ * c.f. WCMC 6.7.2
+ *
+ * XXX add the other dozen class descriptor description structures....
+ *
+ */
+ /*! @{ */
+/*! @struct usbd_header_functional_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief header functional descriptor
+ */
+PACKED0 struct PACKED1 usbd_header_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u16 bcdCDC;
+};
+/*! @struct usbd_call_management_functional_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief call management functional descriptor
+ */
+PACKED0 struct PACKED1 usbd_call_management_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bmCapabilities;
+ u8 bDataInterface;
+};
+/*! @struct usbd_abstract_control_functional_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief abstract control functional descriptor
+ */
+PACKED0 struct PACKED1 usbd_abstract_control_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bmCapabilities;
+};
+/*! @struct usbd_union_functional_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief union functional descriptor
+ */
+PACKED0 struct PACKED1 usbd_union_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bMasterInterface;
+ u8 bSlaveInterface[4];
+};
+/*! @struct usbd_ethernet_networking_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief ethernet networking descriptor
+ */
+PACKED0 struct PACKED1 usbd_ethernet_networking_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ char *iMACAddress;
+ u8 bmEthernetStatistics;
+ u16 wMaxSegmentSize;
+ u16 wNumberMCFilters;
+ u8 bNumberPowerFilters;
+};
+/*! @struct usbd_mobile_direct_line_model_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief mobile direct line model descriptor struct
+ */PACKED0 struct PACKED1 usbd_mobile_direct_line_model_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u16 bcdVersion;
+ u8 bGUID[16];
+};
+/*! @struct usbd_mobile_direct_line_model_detail_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief usbd_mobile direct line model detail descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_mobile_direct_line_model_detail_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bGuidDescriptorType;
+ u8 bDetailData[4];
+};
+/*! @} */
+
+
+
+
+/*!
+ * @name Communications Class Dscriptor Structures
+ * c.f. CDC 5.2 Table 25c
+ */
+
+/*! @{*/
+/*! @struct usbd_class_function_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief CDC class function descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_function_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+};
+/*! @struct usbd_class_function_descriptor_generic usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief CDC calss function descriptor generic struct
+ */
+PACKED0 struct PACKED1 usbd_class_function_descriptor_generic {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bmCapabilities;
+};
+/*! @struct usbd_class_header_function_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class header function descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_header_function_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x00
+ u16 bcdCDC;
+};
+/*! @struct usbd_class_call_management_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class call management descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_call_management_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x01
+ u8 bmCapabilities;
+ u8 bDataInterface;
+};
+/*! @struct usbd_class_abstract_control_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class abstract control descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_abstract_control_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x02
+ u8 bmCapabilities;
+};
+/*! @struct usbd_class_direct_line_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class direct line descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_direct_line_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x03
+};
+/*! @struct usbd_class_telephone_ringer_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class telephone ringer descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_telephone_ringer_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x04
+ u8 bRingerVolSeps;
+ u8 bNumRingerPatterns;
+};
+/*! @struct usbd_class_telephone_call_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class telephone call descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_telephone_call_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x05
+ u8 bmCapabilities;
+};
+/*! @struct usbd_class_union_function_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class union function descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_union_function_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x06
+ u8 bMasterInterface;
+ u8 bSlaveInterface[4];
+};
+/*! @struct usbd_class_country_selection_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class country selection descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_country_selection_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x07
+ u8 iCountryCodeRelDate;
+ u16 wCountryCode0[4];
+};
+
+/*! @struct usbd_class_telephone_operational_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class telephone operational descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_telephone_operational_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x08
+ u8 bmCapabilities;
+};
+
+/*! @struct usbd_class_usbd_terminal_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class usbd terminal descriptor struct
+ *
+ */
+PACKED0 struct PACKED1 usbd_class_usbd_terminal_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x09
+ u8 bEntityId;
+ u8 bInterfaceNo;
+ u8 bOutInterfaceNo;
+ u8 bmOptions;
+ u8 bChild0[4];
+};
+/*! @struct usbd_class_network_channel_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class network channel descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_network_channel_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0a
+ u8 bEntityId;
+ u8 iName;
+ u8 bChannelIndex;
+ u8 bPhysicalInterface;
+};
+/*! @struct usbd_class_protocol_unit_function_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class protocol unit function descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_protocol_unit_function_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0b
+ u8 bEntityId;
+ u8 bProtocol;
+ u8 bChild0[4];
+};
+/*! @struct usbd_class_extension_unit_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class extension unit descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_extension_unit_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0c
+ u8 bEntityId;
+ u8 bExtensionCode;
+ u8 iName;
+ u8 bChild0[4];
+};
+/*! @struct usbd_class_multi_channel_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class multi_channel descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_multi_channel_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0d
+ u8 bmCapabilities;
+};
+/*! @struct usbd_class_capi_control_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class capi control descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_capi_control_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0e
+ u8 bmCapabilities;
+};
+/*! @struct usbd_class_ethernet_networking_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class ethernet networking descriptor
+ */
+PACKED0 struct PACKED1 usbd_class_ethernet_networking_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x0f
+ u8 iMACAddress;
+ u32 bmEthernetStatistics;
+ u16 wMaxSegmentSize;
+ u16 wNumberMCFilters;
+ u8 bNumberPowerFilters;
+};
+/*! @struct usbd_class_atm_networking_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class atm networking descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_atm_networking_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x10
+ u8 iEndSystermIdentifier;
+ u8 bmDataCapabilities;
+ u8 bmATMDeviceStatistics;
+ u16 wType2MaxSegmentSize;
+ u16 wType3MaxSegmentSize;
+ u16 wMaxVC;
+};
+
+/*! @struct usbd_class_mdlm_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class mdlm descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_mdlm_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x12
+ u16 bcdVersion;
+ u8 bGUID[16];
+};
+
+/*
+PACKED0 struct PACKED1 usbd_class_mdlmd_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x13
+ u8 bGuidDescriptorType;
+ u8 bDetailData[4];
+};
+*/
+/*! @struct usbd_class_blan_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class blan descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_blan_descriptor {
+ u8 bFunctionLength; // 0x7
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x13
+ u8 bGuidDescriptorType;
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+ u8 bPad;
+};
+/*! @struct usbd_class_safe_descriptor usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief class safe descriptor struct
+ */
+PACKED0 struct PACKED1 usbd_class_safe_descriptor {
+ u8 bFunctionLength; // 0x6
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype; // 0x13
+ u8 bGuidDescriptorType;
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+};
+
+/*! @} */
+
+
+
+
+/*!
+ * @name EEM
+ * c.f. EEM 1.0
+ */
+/*! @{*/
+
+/*! @struct usbd_eem_packet_header usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief eem packet header wrapper
+ */
+PACKED0 struct PACKED1 usbd_eem_packet_header {
+ u8 bmType:1;
+ u16 eem: 15;
+};
+/*! @struct usbd_eem_data_packet usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief eem data packet wrapper
+ */
+PACKED0 struct PACKED1 usbd_eem_data_packet {
+ u8 bmType:1;
+ u8 bmCRC:1;
+ u16 length:14;
+};
+
+/*! @struct usbd_eem_command_packet usbp-cdc.h "otg/usbp-cdc.h"
+ *
+ * @brief eem command packet wrapper
+ */
+PACKED0 struct PACKED1 usbd_eem_command_packet {
+ u8 bmType:1;
+ u8 bmReserved:4;
+ u16 bmEEMCmdParam:11;
+};
+
+#define EEM_TYPE_DATA 0x0
+#define EEM_TYPE_COMMAND 0x1
+
+#define EEM_ECHO 0x0
+#define EEM_ECHO_RESPONSE 0x1
+#define EEM_SUSPEND_HINT 0x2
+#define EEM_RESPONSE_HINT 0x3
+#define EEM_RESPONSE_COMPLETE_HINT 0x4
+#define EEM_TICKLE 0x5
+
+
+
+
+/*@}*/
+
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
diff --git a/drivers/otg/otg/usbp-chap9.h b/drivers/otg/otg/usbp-chap9.h
new file mode 100644
index 000000000000..7f2411b837ff
--- /dev/null
+++ b/drivers/otg/otg/usbp-chap9.h
@@ -0,0 +1,849 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-chap9.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-chap9.h|20070814003511|14784
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/usbp-chap9.h
+ * @brief Chapter 9 and other class descriptor structure definitions.
+ *
+ * USB Descriptors are used to build a configuration database for each USB
+ * Function driver.
+ *
+ * @ingroup USBDAPI
+ */
+
+/*!
+ * @name Device and/or Interface Class Codes
+ */
+
+ /*! @{ */
+
+
+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
+#define USB_CLASS_AUDIO 1
+#define USB_CLASS_COMM 2
+#define USB_CLASS_HID 3
+#define USB_CLASS_PHYSICAL 5
+#define USB_CLASS_PRINTER 7
+#define USB_CLASS_MASS_STORAGE 8
+#define USB_CLASS_HUB 9
+#define USB_CLASS_DATA 10
+#define USB_CLASS_MISC 0xef
+#define USB_CLASS_APP_SPEC 0xfe
+#define USB_CLASS_VENDOR_SPEC 0xff
+
+/*! @} */
+
+
+/*! @name USB Device Request Types (bmRequestType)
+ *C.f. USB 2.0 Table 9-2
+*/
+
+/*! @{ */
+
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+#ifndef __LINUX_USB_CH9_H
+#define USB_RECIP_HUB 0x00
+#define USB_RECIP_PORT 0x03
+#endif /* __LINUX_USB_CH9_H */
+
+#define USB_DIR_OUT 0
+#define USB_DIR_IN 0x80
+#define USB_ENDPOINT_OPT 0x40
+/*! @} */
+
+/*! @name Descriptor types
+ * C.f. USB Table 9-5
+ */
+
+/*! @{ */
+#ifndef __LINUX_USB_CH9_H
+#define USB_DT_DEVICE 1
+#define USB_DT_CONFIGURATION 2
+#define USB_DT_STRING 3
+#define USB_DT_INTERFACE 4
+#define USB_DT_ENDPOINT 5
+#define USB_DT_DEVICE_QUALIFIER 6
+#define USB_DT_OTHER_SPEED_CONFIGURATION 7
+#define USB_DT_INTERFACE_POWER 8
+#define USB_DT_OTG 9
+#define USB_DT_DEBUG 10
+#define USB_DT_INTERFACE_ASSOCIATION 11
+#define USB_DT_CLASS_SPECIFIC 0x24
+#endif /* __LINUX_USB_CH9_H */
+
+
+#define USB_DT_HID (USB_TYPE_CLASS | 0x01)
+#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02)
+#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
+#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
+/*! @} */
+
+#if 0
+/*! @name Descriptor sizes per descriptor type
+ */
+ /*! @{ */
+
+#define USB_DT_DEVICE_SIZE 18
+#define USB_DT_CONFIG_SIZE 9
+#define USB_DT_INTERFACE_SIZE 9
+#define USB_DT_ENDPOINT_SIZE 7
+#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
+#define USB_DT_HUB_NONVAR_SIZE 7
+#define USB_DT_HID_SIZE 9
+/*! @} */
+#endif
+
+/*! @name Endpoints
+ */
+/*! @{ */
+#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK 0x80
+
+#define USB_ENDPOINT_MASK 0x03 /* in bmAttributes */
+#define USB_ENDPOINT_CONTROL 0x00
+#define USB_ENDPOINT_ISOCHRONOUS 0x01
+#define USB_ENDPOINT_BULK 0x02
+#define USB_ENDPOINT_INTERRUPT 0x03
+/*! @} */
+
+/*!
+ * @name USB Packet IDs (PIDs)
+ */
+/*! @{ */
+#define USB_PID_UNDEF_0 0xf0
+#define USB_PID_OUT 0xe1
+#define USB_PID_ACK 0xd2
+#define USB_PID_DATA0 0xc3
+#define USB_PID_PING 0xb4 /* USB 2.0 */
+#define USB_PID_SOF 0xa5
+#define USB_PID_NYET 0x96 /* USB 2.0 */
+#define USB_PID_DATA2 0x87 /* USB 2.0 */
+#define USB_PID_SPLIT 0x78 /* USB 2.0 */
+#define USB_PID_IN 0x69
+#define USB_PID_NAK 0x5a
+#define USB_PID_DATA1 0x4b
+#define USB_PID_PREAMBLE 0x3c /* Token mode */
+#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
+#define USB_PID_SETUP 0x2d
+#define USB_PID_STALL 0x1e
+#define USB_PID_MDATA 0x0f /* USB 2.0 */
+/*! @} */
+
+/*! * @name Standard requests
+ */
+
+ /*! @{ */
+
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+/*! @} */
+
+/*!
+ * @name USB Spec Release number
+ */
+ /*! @{ */
+
+#define USB_BCD_VERSION_11 0x0110
+#define USB_BCD_VERSION_20 0x0200
+
+#if defined(CONFIG_OTG_HIGH_SPEED) || defined(CONFIG_OTG_BDEVICE_WITH_SRP) || defined(CONFIG_OTG_DEVICE)
+#define USB_BCD_VERSION USB_BCD_VERSION_20
+#else /* CONFIG_OTG_HIGH_SPEED */
+#define USB_BCD_VERSION USB_BCD_VERSION_11
+#endif /* CONFIG_OTG_HIGH_SPEED */
+
+/*! @} */
+
+
+/*!
+ * @name Device Requests (c.f Table 9-2)
+ */
+ /*! @{ */
+
+#define USB_REQ_DIRECTION_MASK 0x80
+#define USB_REQ_TYPE_MASK 0x60
+#define USB_REQ_RECIPIENT_MASK 0x1f
+
+#define USB_REQ_DEVICE2HOST 0x80
+#define USB_REQ_HOST2DEVICE 0x00
+
+#define USB_REQ_TYPE_STANDARD 0x00
+#define USB_REQ_TYPE_CLASS 0x20
+#define USB_REQ_TYPE_VENDOR 0x40
+
+#define USB_REQ_RECIPIENT_DEVICE 0x00
+#define USB_REQ_RECIPIENT_INTERFACE 0x01
+#define USB_REQ_RECIPIENT_ENDPOINT 0x02
+#define USB_REQ_RECIPIENT_OTHER 0x03
+/*! @} */
+
+
+/*!
+ * @name get status bits
+ */
+ /*! @{ */
+
+#define USB_STATUS_SELFPOWERED 0x01
+#define USB_STATUS_REMOTEWAKEUP 0x02
+
+#define USB_STATUS_HALT 0x01
+/*! @} */
+
+/*!
+ * @name Convert Feature Selector to a Status Flag Setting
+ */
+ /*! @{ */
+#define FEATURE(f) (1 << f)
+/*! @} */
+
+/*!
+ * @name Standard Feature Selectors
+ */
+ /*! @{ */
+#ifndef __LINUX_USB_CH9_H
+#define USB_ENDPOINT_HALT 0x00
+#define USB_DEVICE_REMOTE_WAKEUP 0x01
+#endif /* __LINUX_USB_CH9_H */
+#define USB_TEST_MODE 0x02
+/*! @} */
+
+/*!
+ * @name Test Mode Selectors
+ */
+ /*! @{ */
+#define USB_TEST_J 0x1
+#define USB_TEST_K 0x2
+#define USB_TEST_SE0_NAK 0x3
+#define USB_TEST_PACKET 0x4
+#define USB_TEST_FORCE_ENABLE 0x5
+/*! @} */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+/* Note:
+The various structs declared here need 1-byte
+alignment. These is achieved in various ways
+with assorted C compilers.
+First of all, some compilers need the pack()
+#pragma. This is enabled by defining the PRAGMAPACK
+macro
+
+Secondly the structure definitions themselves
+may require specialized additions to their declarations.
+The following model seems to accommodate all compilers
+known so far:
+
+
+PACKED0 struct PACKED1 {
+ various items
+};
+
+Define appropriate values for PACKED0, PACKED1, PACKED2
+suited to your compiler. In most cases at least two of
+these values will be empty strings.
+
+*/
+
+/*! @struct usbd_device_request usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief usb device request wrapper struct
+ */
+PACKED0 struct PACKED1 usbd_device_request {
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+};
+
+
+/*! @struct usbd_otg_descriptor usbp-chap9.h "otg/osbp-chap9.h"
+ *
+ * @brief otg descriptor wrapper
+ */
+PACKED0 struct PACKED1 usbd_otg_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bmAttributes;
+};
+
+/*!
+ * @name OTG
+ */
+ /*! @{ */
+#define USB_OTG_HNP_SUPPORTED 0x02
+#define USB_OTG_SRP_SUPPORTED 0x01
+/*! @} */
+
+/*!
+ * @name OTG Feature selectors
+ */
+ /*! @{ */
+#define USB_OTG_B_HNP_ENABLE 0x03
+#define USB_OTG_A_HNP_SUPPORT 0x04
+#define USB_OTG_A_ALT_HNP_ENABLE 0x05
+/*! @} */
+
+
+/*!
+ * @name Endpoint Modifiers
+ * static struct usbd_endpoint_description function_default_A_1[] = {
+ *
+ * {this_endpoint: 0, attributes: CONTROL, max_size: 8, polling_interval: 0 },
+ * {this_endpoint: 1, attributes: BULK, max_size: 64, polling_interval: 0, direction: IN},
+ * {this_endpoint: 2, attributes: BULK, max_size: 64, polling_interval: 0, direction: OUT},
+ * {this_endpoint: 3, attributes: INTERRUPT, max_size: 8, polling_interval: 0},
+ *
+ *
+ */
+ /*! @{ */
+
+#define CONTROL 0x00
+#define ISOCHRONOUS 0x01
+#define BULK 0x02
+#define INTERRUPT 0x03
+/*! @} */
+
+
+/*! @name configuration modifiers
+ */
+
+/* @{ */
+#define USB_BMATTRIBUTE_RESERVED 0x80
+#define USB_BMATTRIBUTE_SELF_POWERED 0x40
+#define USB_BMATTRIBUTE_REMOTE_WAKEUP 0x20
+
+#if 0
+/*
+ * The UUT tester specifically tests for MaxPower to be non-zero (> 0).
+ */
+#if !defined(CONFIG_OTG_MAXPOWER) || (CONFIG_OTG_MAXPOWER == 0)
+ #define BMATTRIBUTE BMATTRIBUTE_RESERVED | BMATTRIBUTE_SELF_POWERED
+ #define BMAXPOWER 1
+#else
+ #define BMATTRIBUTE BMATTRIBUTE_RESERVED
+ #define BMAXPOWER CONFIG_USBD_MAXPOWER
+#endif
+#endif
+/*! @} */
+
+
+
+/*!
+ * @name Standard Usb Descriptor Structures
+ */
+ /*! @{ */
+/*! @struct usbd_endpoint_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief endpoint descriptor encapsulation
+ */
+PACKED0 struct PACKED1 usbd_endpoint_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x5
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+ u16 wMaxPacketSize;
+ u8 bInterval;
+};
+/*! @struct usbd_interface_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief interface descriptor structure
+ */
+PACKED0 struct PACKED1 usbd_interface_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x04
+ u8 bInterfaceNumber;
+ u8 bAlternateSetting;
+ u8 bNumEndpoints;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ u8 bInterfaceProtocol;
+ u8 iInterface;
+};
+/*! @struct usbd_configuration_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief configuration descriptor structure
+ */
+PACKED0 struct PACKED1 usbd_configuration_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x2
+ u16 wTotalLength;
+ u8 bNumInterfaces;
+ u8 bConfigurationValue;
+ u8 iConfiguration;
+ u8 bmAttributes;
+ u8 bMaxPower;
+};
+/*! @struct usbd_device_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief device descriptor structure
+ */
+PACKED0 struct PACKED1 usbd_device_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x01
+ u16 bcdUSB;
+ u8 bDeviceClass;
+ u8 bDeviceSubClass;
+ u8 bDeviceProtocol;
+ u8 bMaxPacketSize0;
+ u16 idVendor;
+ u16 idProduct;
+ u16 bcdDevice;
+ u8 iManufacturer;
+ u8 iProduct;
+ u8 iSerialNumber;
+ u8 bNumConfigurations;
+};
+
+/* Define according to the whims of the compiler */
+#define USBD_WDATA_MIN 1 /* Some compilers will allow 0 */
+/*! @struct usbd_string_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief string descriptor structure
+ */
+PACKED0 struct PACKED1 usbd_string_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x03
+ u16 wData[USBD_WDATA_MIN];
+};
+/*! @struct usbd_langid_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief language ID descriptor
+ */
+PACKED0 struct PACKED1 usbd_langid_descriptor {
+ u8 bLength;
+ u8 bDescriptorType; // 0x03
+ u8 bData[2];
+};
+
+#if (USBD_WDATA_MIN > 0)
+#define USBD_STRING_SIZEOF(x) (sizeof(x) - 2*(USBD_WDATA_MIN))
+#else
+#define USBD_STRING_SIZEOF(x) (sizeof(x))
+#endif
+
+/*! @struct usbd_device_qualifier_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief device qualifier descriptor
+ */
+struct usbd_device_qualifier_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bcdUSB;
+ u8 bDeviceClass;
+ u8 bDeviceSubClass;
+ u8 bDeviceProtocol;
+ u8 bMaxPacketSize0;
+ u8 bNumConfigurations;
+ u8 bReserved;
+};
+
+/*! @struct usbd_generic_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief usb generic descriptor structure
+ */
+struct usbd_generic_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+};
+/*! @struct usbd_generic_calss_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief generic class descriptor structure
+ */
+struct usbd_generic_class_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+};
+/*! @} */
+
+/*!
+ * @name Interface Association Descriptor
+ */
+ /*! @{ */
+/*! @struct usbd_interface_association_descriptor usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief interface association descriptor
+ */
+PACKED0 struct PACKED1 usbd_interface_association_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bFirstInterface;
+ u8 bInterfaceCount;
+ u8 bFunctionClass;
+ u8 bFunctionSubClass;
+ u8 bFunctionProtocol;
+ u8 iFunction;
+};
+
+/*! @} */
+
+#if 0
+/*!
+ * @name Descriptor Union Structures
+ */
+ /*! @{ */
+
+struct usbd_descriptor {
+ union {
+ struct usbd_generic_descriptor generic_descriptor;
+ struct usbd_generic_class_descriptor generic_class_descriptor;
+ struct usbd_endpoint_descriptor endpoint_descriptor;
+ struct usbd_interface_descriptor interface_descriptor;
+ struct usbd_configuration_descriptor configuration_descriptor;
+ struct usbd_device_descriptor device_descriptor;
+ struct usbd_string_descriptor string_descriptor;
+ } descriptor;
+
+};
+
+struct usbd_class_descriptor {
+ union {
+ struct usbd_class_function_descriptor function;
+ struct usbd_class_function_descriptor_generic generic;
+ struct usbd_class_header_function_descriptor header_function;
+ struct usbd_class_call_management_descriptor call_management;
+ struct usbd_class_abstract_control_descriptor abstract_control;
+ struct usbd_class_direct_line_descriptor direct_line;
+ struct usbd_class_telephone_ringer_descriptor telephone_ringer;
+ struct usbd_class_telephone_operational_descriptor telephone_operational;
+ struct usbd_class_telephone_call_descriptor telephone_call;
+ struct usbd_class_union_function_descriptor union_function;
+ struct usbd_class_country_selection_descriptor country_selection;
+ struct usbd_class_usbd_terminal_descriptor usb_terminal;
+ struct usbd_class_network_channel_descriptor network_channel;
+ struct usbd_class_extension_unit_descriptor extension_unit;
+ struct usbd_class_multi_channel_descriptor multi_channel;
+ struct usbd_class_capi_control_descriptor capi_control;
+ struct usbd_class_ethernet_networking_descriptor ethernet_networking;
+ struct usbd_class_atm_networking_descriptor atm_networking;
+ struct usbd_class_mdlm_descriptor mobile_direct;
+ //struct usbd_class_mdlmd_descriptor mobile_direct_detail;
+ struct usbd_class_blan_descriptor mobile_direct_blan_detail;
+ struct usbd_class_safe_descriptor mobile_direct_safe_detail;
+ } descriptor;
+
+};
+/*! @} */
+#endif
+
+
+/*!
+ * Device Events
+ *
+ * These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1).
+ *
+ * There are additional events defined to handle some extra actions we need to have handled.
+ *
+ */
+/*! create a type for usbd_device_event */
+typedef enum usbd_device_event {
+
+ DEVICE_UNKNOWN, // 0 - bi - unknown event
+ DEVICE_INIT, // 1 - bi - initialize
+ DEVICE_CREATE, // 2 - bi -
+ DEVICE_HUB_CONFIGURED, // 3 - bi - bus has been plugged int
+ DEVICE_RESET, // 4 - bi - hub has powered our port
+
+ DEVICE_ADDRESS_ASSIGNED, // 5 - ep0 - set address setup received
+ DEVICE_CONFIGURED, // 6 - ep0 - set configure setup received
+ DEVICE_SET_INTERFACE, // 7 - ep0 - set interface setup received
+
+ DEVICE_SET_FEATURE, // 8 - ep0 - set feature setup received
+ DEVICE_CLEAR_FEATURE, // 9 - ep0 - clear feature setup received
+
+ DEVICE_DE_CONFIGURED, // 10 - ep0 - set configure setup received for ??
+
+ DEVICE_BUS_INACTIVE, // 11 - bi - bus in inactive (no SOF packets)
+ DEVICE_BUS_ACTIVITY, // 12 - bi - bus is active again
+
+ DEVICE_POWER_INTERRUPTION, // 13 - bi - hub has depowered our port
+ DEVICE_HUB_RESET, // 14 - bi - bus has been unplugged
+ DEVICE_DESTROY, // 15 - bi - device instance should be destroyed
+ DEVICE_CLOSE, // 16 - bi - device instance should be destroyed
+
+} usbd_device_event_t;
+
+/*! USB Request Block structure
+ *
+ * This is used for both sending and receiving data.
+ *
+ * The callback function is used to let the function driver know when
+ * transmitted data has been sent.
+ *
+ * The callback function is set by the alloc_recv function when an urb is
+ * allocated for receiving data for an endpoint and used to call the
+ * function driver to inform it that data has arrived.
+ *
+ * Note that for OUT urbs the buffer is always allocated to a multiple of
+ * the packetsize that is 1 larger than the requested size. This prevents
+ * overflow if the host unexpectedly sends a full sized packet when we are
+ * expecting a short one (the host is always right..)
+ *
+ * USBD_URB_SENDZLP - set this flag if a ZLP (zero length packet) should be
+ * sent after the data is sent. This is required if sending data that is a
+ * multiple of the packetsize but less than the maximum transfer size for
+ * the protocol in use. A ZLP is required to ensure that the host
+ * recognizes a short transfer.
+ *
+ */
+/*!create a type for urb_link */
+typedef struct urb_link {
+ u32 total;
+ struct urb_link *next;
+ struct urb_link *prev;
+} urb_link;
+
+
+/*! URB Status
+ *
+ * This defines the current status of a pending or finshed URB.
+ *
+ */
+/*! create a type for usbd_urb_status
+ * @brief typedef enum usbd_urb_status usbd_urb_status_t
+ */
+typedef enum usbd_urb_status {
+ USBD_URB_OK = 0,
+ USBD_URB_QUEUED,
+ USBD_URB_ACTIVE,
+ USBD_URB_CANCELLED,
+ USBD_URB_ERROR,
+ USBD_URB_STALLED,
+ USBD_URB_RESET,
+ USBD_URB_NOT_READY,
+ USBD_URB_DISABLED,
+} usbd_urb_status_t;
+
+#define USBD_URB_SENDZLP (1 << 0) /* send a Zero Length Packet when urb is finished */
+#define USBD_URB_ZLPSENT (1 << 1) /* send a Zero Length Packet when urb is finished */
+#define USBD_URB_SOF (1 << 2) /* terminate a receive transfer at SOF if any data received */
+#define USBD_URB_SOF2 (1 << 3) /* terminate a receive transfer at SOF if no data since previous SOF
+ (some data was received) */
+
+#define USBD_URB_FAST_RETURN (1 << 4) /* process finished urb in interrupt */
+#define USBD_URB_FAST_FINISH (1 << 5) /* finished tx_urb as soon as loaded into FIFO */
+
+#define USBD_URB_OUT (1 << 6)
+#define USBD_URB_IN (1 << 7)
+
+/*
+ * These are used by the PCD hardware layer to indicate current
+ * status of the urb. The lifecycle of the urb is:
+ *
+ * 1. start urb - set USBD_URB_READY, add to endpoint queue
+ *
+ * 2. pcd - set to USBD_URB_ACTIVE when starting to process
+ *
+ * 3. pcd - set to USBD_URB_FINISHED when finished processing
+ *
+ */
+#define USBD_URB_READY (1 << 0) /* urb is ready */
+#define USBD_URB_ACTIVE (1 << 1) /* urb is active */
+#define USBD_URB_FINISHED (1 << 2) /* urb is finished */
+
+//#define USBD_URB_OK (1 << 5)
+//#define USBD_URB_CANCELLED (1 << 6)
+//#define USBD_URB_ERROR (1 << 7)
+
+
+
+/*! @struct usbd_urb usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief definition of the structure itself
+ */
+struct usbd_urb {
+
+ struct usbd_bus_instance *bus;
+ struct usbd_function_instance *function_instance;
+ struct usbd_endpoint_instance *endpoint;
+
+
+ //usbd_urb_notification *notify;
+ int (*notify) (struct usbd_urb *, int);
+
+ u8 *buffer; // data received (OUT) or being sent (IN)
+ dma_addr_t dma_addr;
+ u32 buffer_length; // maximum data expected for OUT
+ u32 alloc_length; // allocated size of buffer
+ u32 actual_length; // actual data received (OUT or being sent (IN)
+ u32 flags;
+ u32 irq_flags;
+
+ int endpoint_index;
+ u16 wMaxPacketSize;
+
+ void *function_privdata;
+ void *bus_privdata;
+
+ struct urb_link link;
+ usbd_urb_status_t status; // what is the current status of the urb
+ otg_tick_t ticks;
+ u16 framenum; // SOF framenum when urb was finished
+
+#ifdef CONFIG_OTG_LATENCY_CHECK
+ otg_tick_t urb_rcved_ticks; // the queued time
+ otg_tick_t bh_start_ticks; // The process finished time
+ otg_tick_t goto_net_ticks; // the queued time
+ otg_tick_t usbd_ticks_start; // The process finished time
+ otg_tick_t usbd_ticks_end; // The process finished time
+#endif
+};
+
+
+/*! typedef int usbd_urb_notification(struct usbd_urb, int)
+ *
+ * @brief Standard notification callback typedef
+
+ * This function, if present, is called to notify the function that the low layer
+ * is finished with the URB.
+ * If the function is absent or returns nonzero, then the URB is deallocated
+ * Otherwise the URB is preserved (for possible reuse by the function)
+ * The notification function is supplied an argument equal to the urb->status field.
+ */
+typedef int usbd_urb_notification(struct usbd_urb *urb, int rc);
+
+/*! @struct usbd_endpoint_request usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief Endpoint Request struct *
+ * An array of these structures is initialized by each function driver to specify the endpoints
+ * it requires.
+ *
+ * The bus interface driver will attempt to fulfill these requests with the actual endpoints it
+ * has available.
+ *
+ * Note that in most cases the bEndpointAddress should be left as zero except for specialized
+ * function drivers that both require a specific value and know ahead of time that the specified
+ * value is legal for the bus interface driver being used.
+ */
+struct usbd_endpoint_request {
+ u8 index; /* function specific index */
+ u8 configuration; /* configuration endpoint will be in */
+ u8 interface_num; /* interface endpoint will be in */
+ u8 alternate; /* altsetting for this request */
+ u8 bmAttributes; /* endpoint type AND direction */
+ u16 fs_requestedTransferSize; /* max full speed transfer size for this endpoint */
+ u16 hs_requestedTransferSize; /* max high speed transfer size for this endpoint */
+ u8 bInterval;
+ u8 bEndpointAddress; /* specific bEndpointAddress function driver requires */
+ u8 physical; /* physical endpoint used */
+};
+
+/*!@struct usbd_endpoint_map usbp-chap9.h "otg/usbp-chap9.h"
+ *
+ * @brief Endpoint Map *
+ * An array of these structures is created by the bus interface driver to
+ * show what endpoints have been configured for the function driver.
+ *
+ * This is the logical array of endpoints that are indexed into by the
+ * function layer using the logical endpoint numbers defined by the function
+ * drivers. It maps these numbers into a real physical endpoint.
+ *
+ * Fields that can be different for Full speed versus High speed are
+ * represented with an array both values are available.
+ *
+ * For interfaces with multiple alternate settings, each separate
+ * combinantion of interface_num/altsetting/endpoint must be specified.
+ * It is up to the lower layers to determine if / when overlapped
+ * endpoints can be re-used.
+ *
+ */
+struct usbd_endpoint_map {
+ u8 index;
+ u8 configuration;
+ u8 interface_num;
+ u8 alternate;
+ u8 bEndpointAddress[2]; // logical endpoint address
+ u16 wMaxPacketSize[2]; // packetsSize for requested endpoint
+ u8 bmAttributes[2]; // requested endpoint type
+ u16 transferSize[2]; // transferSize for bulk transfers
+ u8 physicalEndpoint[2]; // physical endpoint number
+ u8 bInterval[2];
+ struct usbd_endpoint_instance *endpoint;
+};
+
+
+/*!
+ * create a type for enum usbd_device_status
+ * @brief Device status enumeration *
+ * Overall state, we use this to show when we are suspended.
+ * This is required because when resumed we need to go back
+ * to previous state.
+ */
+typedef enum usbd_device_status {
+ USBD_OPENING, // 0. we are currently opening
+ USBD_RESETING, // 1. we are currently opening
+ USBD_OK, // 2. ok to use
+ USBD_SUSPENDED, // 3. we are currently suspended
+ USBD_CLOSING, // 4. we are currently closing
+ USBD_CLOSED, // 5. we are currently closing
+ USBD_UNKNOWN,
+} usbd_device_status_t;
+
+
+/*! @var typedef enum usbd_device_state usbd_device_state_t
+ * Device State (c.f USB Spec 2.0 Figure 9-1)
+ *
+ * What state the usb device is in.
+ *
+ * Note the state does not change if the device is suspended, we simply set a
+ * flag to show that it is suspended.
+ *
+ */
+typedef enum usbd_device_state {
+ STATE_INIT, // 0. just initialized
+ STATE_CREATED, // 1. just created
+ STATE_ATTACHED, // 2. we are attached
+ STATE_POWERED, // 3. we have seen power indication (electrical bus signal)
+ STATE_DEFAULT, // 4. we been reset
+ STATE_ADDRESSED, // 5. we have been addressed (in default configuration)
+ STATE_CONFIGURED, // 6. we have seen a set configuration device command
+ STATE_SUSPENDED, // 7. device has been suspended
+ STATE_UNKNOWN, // 8. destroyed
+} usbd_device_state_t;
+
+
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
diff --git a/drivers/otg/otg/usbp-func.h b/drivers/otg/otg/usbp-func.h
new file mode 100644
index 000000000000..9c51dff73056
--- /dev/null
+++ b/drivers/otg/otg/usbp-func.h
@@ -0,0 +1,737 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-func.h - USB Device Function Driver Interface
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-func.h|20070220211522|09278
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ *
+ */
+
+/*!
+ * @file otg/otg/usbp-func.h
+ * @brief Function Driver related structures and definitions.
+ *
+ * This file contains the USB Function Driver and USB Interface Driver
+ * definitions.
+ *
+ * The USBOTG support allows for implementation of composite devices.
+ *
+ * From USB 2.0, C.f. 5.2.3:
+ *
+ * A Device that has multiple interfaces controlled independantly of
+ * each other is referred to as a composite device. A composite device
+ * has only a single device.
+ *
+ * In this implemenation the function portion of the device is split into
+ * two types of drivers:
+ *
+ * - USB Interface Driver
+ * - USB Function Driver
+ *
+ *
+ * USB Interface Driver
+ *
+ * An USB Interface Driver specifies the Interface related descriptors and
+ * implements the data handling processes for each of the data endpoints
+ * associated with the interface (or interfaces) required for the function.
+ * It typically will also implement the upper edge interface to the OS.
+ *
+ * The USB Interface Driver will also handle non-standard device requests
+ * and feature requests that are for the interface or an endpoint associated
+ * with one of the interfaces associated with the driver.
+ *
+ * Each interface implemented by an USB Interface Driver will have a
+ * interface instance that stores a pointer to the parent USB Function
+ * Driver and has an array of pointers to endpoint instances for the data
+ * endpoints associated with the interface.
+ *
+ *
+ * USB Function Driver
+ *
+ * A USB Function Driver specifies the device and configuration descriptors.
+ * A configuration descriptor is assembled from the interface descriptors
+ * from the USB Interface Driver (or drivers) that it is making available to
+ * the USB Host.
+ *
+ * The USB Function Driver handles non-standard device requests and feature
+ * requests for the device.
+ *
+ * Each function driver has a function instance that maintains an array
+ * of pointers to configuration instances and a pointer to the device
+ * descriptor.
+ *
+ * Each configuration instance maintains an array of pointers to the
+ * interface instances for that configuration and a pointer to the
+ * complete configuration descriptor.
+ *
+ * @ingroup USBDAPI
+ */
+
+#ifndef USBP_FUNC_H
+#define USBP_FUNC_H
+
+/*!
+ * This file contains the USB Device Core Common definitions. It also contains definitions for
+ * functions that are commonly used by both Function Drivers and Bus Interface Drivers.
+ *
+ * Specifically:
+ *
+ * o public functions exported by the USB Core layer
+ *
+ * o common structures and definitions
+ *
+ */
+
+
+/*!
+ * @var typedef enum usbd_function_types usbd_function_types_t
+ *
+ * @brief USB Function Driver types
+ */
+typedef enum usbd_function_types {
+ function_ep0, // combined driver
+ function_simple, // combined driver
+ function_interface, // supports interface
+ function_class, // supports class
+ function_composite, // supports configuration
+} usbd_function_types_t;
+
+
+/*!@struct usbd_function_operations usbp-func.h "otg/usbp-func.h"
+ * USB Function Driver structures
+ *
+ * Descriptors:
+ * struct usbd_endpoint_description
+ * struct usbd_interface_description
+ * struct usbd_configuration_description
+ *
+ * Driver description:
+ * struct usbd_function_driver
+ * struct usbd_function_operations
+ *
+ */
+
+struct usbd_function_operations {
+
+ int (*function_enable) (struct usbd_function_instance *);
+ void (*function_disable) (struct usbd_function_instance *);
+
+ /*
+ * All of the following may be called from an interrupt context
+ */
+ void (*event_handler) (struct usbd_function_instance *, usbd_device_event_t, int);
+ int (*device_request) (struct usbd_function_instance *, struct usbd_device_request *);
+
+ // XXX should these return anything?
+ int (*set_configuration) (struct usbd_function_instance *, int configuration);
+ int (*set_interface) (struct usbd_function_instance *, int wIndex, int altsetting);
+ int (*suspended) (struct usbd_function_instance *);
+ int (*resumed) (struct usbd_function_instance *);
+ int (*reset) (struct usbd_function_instance *);
+
+ void * (*get_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index);
+ int * (*set_descriptor)(struct usbd_function_instance *, int descriptor_type, int descriptor_index, void *);
+
+ void (*endpoint_cleared)(struct usbd_function_instance*, int wIndex);
+
+};
+
+
+/*!@struct xusbd_alternate_instance usbp-func.h "otg/usbp-func.h"
+ * function driver definitions
+ */
+struct xusbd_alternate_instance {
+ struct usbd_interface_descriptor *interface_descriptor;
+ int classes;
+ struct usbd_generic_class_descriptor **class_list;
+ int endpoints;
+};
+
+
+/*!
+ * @struct usbd_alternate_description usbp-func.h "otg/usbp-func.h"
+ *
+ * @brief usb device description structures
+ */
+
+struct usbd_alternate_description {
+ //struct usbd_interface_descriptor *interface_descriptor;
+ char *iInterface;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ u8 bInterfaceProtocol;
+
+ // list of CDC class descriptions for this alternate interface
+ u8 classes;
+ struct usbd_generic_class_descriptor **class_list;
+
+ // list of endpoint descriptions for this alternate interface
+ u8 endpoints;
+
+ // list of indexes into endpoint request map for each endpoint descriptor
+ u8 *endpoint_index;
+
+ // XXX This should be here
+ u8 new_endpointsRequested;
+ struct usbd_endpoint_request *new_requestedEndpoints;
+};
+
+/*! create a type for usbd_generic_class_defscriptor
+ * @brief typedef struct usbd_generic_class_descriptor usbd_calss_descriptor_t
+ *
+ */
+
+typedef struct usbd_generic_class_descriptor usbd_class_descriptor_t;
+
+/*!@var typedef struct usbd_endpoint_descriptor usbd_endpoint_descriptor_t;
+ *
+ * @brief create a type for usbd_endpoint_descriptor
+ */
+typedef struct usbd_endpoint_descriptor usbd_endpoint_descriptor_t;
+
+/*! @struct usbd_interface_description usbp-func.h "otg/usbp-func.h"
+ */
+
+struct usbd_interface_description {
+ // list of alternate interface descriptions for this interface
+ u8 alternates; /*< number of alternates */
+ struct usbd_alternate_description *alternate_list; /*< alternate description list */
+};
+
+/*! @struct usbd_configuration_description usbp-func.h "otg/usbp-func.h"*/
+
+struct usbd_configuration_description {
+ char *iConfiguration; /*< configuration index string */
+ u8 bmAttributes; /*< bmAttributes */
+ u8 bMaxPower; /*< bMaxPower - maximum power allowed */
+};
+
+/*! @struct usbd_device_description usbp-func.h "otg/usbp-func.h"
+ */
+
+struct usbd_device_description {
+ //struct usbd_device_descriptor *device_descriptor;
+ //struct usbd_otg_descriptor *otg_descriptor;
+
+ u16 idVendor;
+ u16 idProduct;
+ u16 bcdDevice;
+ u8 bDeviceClass;
+ u8 bDeviceSubClass;
+ u8 bDeviceProtocol;
+
+ u8 bMaxPacketSize0;
+
+ u8 *iManufacturer;
+ u8 *iProduct;
+ u8 *iSerialNumber;
+};
+/*! @struct usbd_instances_instance usbp-func.h "otg/usbp-func.h"*/
+
+struct usbd_interfaces_instance {
+ u8 alternates;
+ struct usbd_alternate_instance *alternates_instance_array;
+};
+
+/*! @struct usbd_function_driver usbp-func.h "otg/usbp-func.h"
+ *
+ * @brief Function Driver data structure * Function driver and its configuration descriptors.
+ *
+ * This is passed to the usb-device layer when registering. It contains all
+ * required information about the function driver for the usb-device layer
+ * to use the function drivers configuration data and to configure this
+ * function driver an active configuration.
+ *
+ * Note that each function driver registers itself on a speculative basis.
+ * Whether a function driver is actually configured will depend on the USB
+ * HOST selecting one of the function drivers configurations.
+ *
+ * This may be done multiple times WRT to either a single bus interface
+ * instance or WRT to multiple bus interface instances. In other words a
+ * multiple configurations may be selected for a specific bus interface. Or
+ * the same configuration may be selected for multiple bus interfaces.
+ *
+ */
+#define FUNCTION_REGISTERED 0x1 // set in flags if function is registered
+#define FUNCTION_ENABLED 0x2 // set in flags if function is enabled
+struct usbd_function_driver {
+ struct otg_list_node drivers; // linked list (was drivers in usbd_function_driver)
+ const char *name;
+ struct usbd_function_operations *fops; // functions
+ usbd_function_types_t function_type;
+ u32 flags;
+ void *privdata;
+ const char **usb_strings;
+ u8 *usb_string_indexes;
+};
+
+/*!@struct usbd_function_instance usbp-func.h "otg/usbp-func.h"
+ *
+ * @brief function configuration structure
+ *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usbd_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usbd_configuration_description and
+ * usbd_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ * The endpoint map will contain a list of all endpoints for the configuration
+ * driver, and only related endpoints for an interface driver.
+ *
+ * The interface driver array will be NULL for an interface driver.
+ */
+struct usbd_function_instance {
+ const char *name;
+ struct usbd_bus_instance *bus;
+ usbd_function_types_t function_type;
+ struct usbd_function_driver *function_driver;
+ void *privdata; // private data for the function
+
+ //int usbd_maxstrings;
+ //struct usbd_string_descriptor **usb_strings;
+};
+
+/*! @struct usbd_simple_driver usbp-func.h "otg/usbp-func.h"
+ *
+ * @brief Function Driver data structure *
+ * Function driver and its configuration descriptors.
+ *
+ * This is passed to the usb-device layer when registering. It contains all
+ * required information about the function driver for the usb-device layer
+ * to use the function drivers configuration data and to configure this
+ * function driver an active configuration.
+ *
+ * Note that each function driver registers itself on a speculative basis.
+ * Whether a function driver is actually configured will depend on the USB
+ * HOST selecting one of the function drivers configurations.
+ *
+ * This may be done multiple times WRT to either a single bus interface
+ * instance or WRT to multiple bus interface instances. In other words a
+ * multiple configurations may be selected for a specific bus interface. Or
+ * the same configuration may be selected for multiple bus interfaces.
+ *
+ */
+struct usbd_simple_driver {
+
+ struct usbd_function_driver driver;
+
+ // device & configuration descriptions
+ struct usbd_device_description *device_description;
+ struct usbd_configuration_description *configuration_description;
+ int bNumConfigurations;
+
+ u8 interfaces; // XXX should be interfaces
+ struct usbd_interface_description *interface_list;
+
+ u8 endpointsRequested;
+ struct usbd_endpoint_request *requestedEndpoints;
+
+ // constructed descriptors
+ //struct usbd_device_descriptor *device_descriptor;
+
+ u8 iManufacturer;
+ u8 iProduct;
+ u8 iSerialNumber;
+
+
+ //struct usbd_otg_descriptor *otg_descriptor;
+ //struct usbd_configuration_instance *configuration_instance_array;
+
+};
+
+/*! @struct usbd_simple_instance usbp-func.h "otg/usbp-func.h"
+ *
+ * @brief Simple configuration structure *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usbd_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usbd_configuration_description and
+ * usbd_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ * The endpoint map will contain a list of all endpoints for the configuration
+ * driver, and only related endpoints for an interface driver.
+ *
+ * The interface driver array will be NULL for an interface driver.
+ */
+struct usbd_simple_instance {
+ // common to all types
+ struct usbd_function_instance function;
+
+ // composite driver only
+ int configuration; // saved value from set configuration
+
+ int interface_functions; // number of names in interfaces_list
+ int interfaces; // accumulated total of all bNumInterfaces
+
+
+ u8 *altsettings; // array[0..interfaces-1] of alternate settings
+
+ struct usbd_endpoint_map *endpoint_map_array;
+
+ int configuration_size;
+ struct usbd_configuration_descriptor *configuration_descriptor[2];
+ u8 ConfigurationValue; // current set configuration (zero is default)
+
+};
+
+/*!@struct usbd_class_instance usbp-func.h "otg/usbp-func.h"
+ * @brief class configuration structure
+ *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usbd_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usbd_configuration_description and
+ * usbd_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ * The endpoint map will contain a list of all endpoints for the configuration
+ * driver, and only related endpoints for an interface driver.
+ *
+ * The interface driver array will be NULL for an interface driver.
+ */
+struct usbd_class_instance {
+ struct usbd_function_instance function;
+
+};
+
+/*!@struct usbd_class_driver usbp-func.h "otg/usbp-func.h" */
+
+struct usbd_class_driver {
+ struct usbd_function_driver driver;
+};
+
+
+
+/*! @struct usbd_interface_driver usbp-func.h "otg/usbp-func.h"
+ * @brief interface configuration structure
+ *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usbd_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usbd_configuration_description and
+ * usbd_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ * The endpoint map will contain a list of all endpoints for the configuration
+ * driver, and only related endpoints for an interface driver.
+ *
+ * The interface driver array will be NULL for an interface driver.
+ */
+
+struct usbd_interface_driver {
+ struct usbd_function_driver driver;
+
+ u8 interfaces;
+ struct usbd_interface_description *interface_list;
+
+ // XXX This should moved to interface description
+ u8 endpointsRequested;
+ struct usbd_endpoint_request *requestedEndpoints;
+
+ u8 bFunctionClass;
+ u8 bFunctionSubClass;
+ u8 bFunctionProtocol;
+ char *iFunction;
+};
+
+/*!@struct usbd_interface_instance usbp-func.h "otg/usbp-func.h"*/
+
+struct usbd_interface_instance {
+ struct usbd_function_instance function;
+
+ int wIndex; // allocated interface number
+ int lIndex; // logical interface number
+ int altsetting;
+
+ int endpoints;
+ struct usbd_endpoint_map *endpoint_map_array;
+};
+
+/*! @struct usbd_composite_driver usbp-func.h "otg/usbp-func.h"
+ * @brief Composite configuration structure
+ *
+ * This is allocated for each configured instance of a function driver.
+ *
+ * It stores pointers to the usbd_function_driver for the appropriate function,
+ * and pointers to the USB HOST requested usbd_configuration_description and
+ * usbd_interface_description.
+ *
+ * The privdata pointer may be used by the function driver to store private
+ * per instance state information.
+ *
+ * The endpoint map will contain a list of all endpoints for the configuration
+ * driver, and only related endpoints for an interface driver.
+ *
+ * The interface driver array will be NULL for an interface driver.
+ */
+struct usbd_composite_driver {
+ struct usbd_function_driver driver;
+
+ // device & configuration descriptions
+ struct usbd_device_description *device_description;
+ struct usbd_configuration_description *configuration_description;
+ int bNumConfigurations;
+
+ const char **interface_function_names; // list of interface function names
+ const char *class_name;
+ const char *windows_os; // windows 0xee string
+
+ u8 iManufacturer;
+ u8 iProduct;
+ u8 iSerialNumber;
+
+ //struct usbd_otg_descriptor *otg_descriptor; // XXX unused
+
+};
+
+/*! @struct usbd_composite_instance usbp-func.h "otg/usbp-func.h"*/
+struct usbd_composite_instance {
+ // common to all types
+ struct usbd_function_instance function;
+ // composite driver only
+ int configuration; // saved value from set configuration
+
+ int interface_functions; // number of interface functions available
+ int interfaces; // accumulated total of all bNumInterfaces
+
+ struct usbd_interface_description *class_list;
+ struct usbd_class_instance *class_instance;
+
+ struct usbd_interface_instance *interfaces_array; // array of interface instances, one per interface function
+ struct usbd_interface_description *interface_list;
+
+ int endpoints;
+ struct usbd_endpoint_map *endpoint_map_array;
+ u8 endpointsRequested;
+ struct usbd_endpoint_request *requestedEndpoints;
+
+ int configuration_size;
+ struct usbd_configuration_descriptor *configuration_descriptor[2];
+ u8 ConfigurationValue; // current set configuration (zero is default)
+};
+/*
+union usbd_instance_union {
+ struct usbd_function_instance *function_instance;
+ struct usbd_simple_instance *simple_instance;
+ struct usbd_composite_instance *composite_instance;
+};
+*/
+/*!
+ * @name Function Driver Registration
+ *
+ * Called by function drivers to register themselves when loaded
+ * or de-register when unloading.
+ * @{
+ */
+int usbd_register_simple_function (struct usbd_simple_driver *, const char *, void *);
+
+int usbd_register_interface_function (struct usbd_interface_driver *, const char *, void *);
+int usbd_register_class_function (struct usbd_class_driver *function_driver, const char*, void *);
+int usbd_register_composite_function (struct usbd_composite_driver *function_driver, const char*, const char*, const char **, void *);
+
+void usbd_deregister_simple_function (struct usbd_simple_driver *);
+void usbd_deregister_interface_function (struct usbd_interface_driver *);
+void usbd_deregister_class_function (struct usbd_class_driver *);
+void usbd_deregister_composite_function (struct usbd_composite_driver *);
+
+struct usbd_function_driver *usbd_find_function (const char *name);
+struct usbd_interface_driver *usbd_find_interface_function(const char *name);
+struct usbd_class_driver *usbd_find_class_function(const char *name);
+struct usbd_composite_driver *usbd_find_composite_function(const char *name);
+
+//struct usbd_function_instance * usbd_alloc_function (struct usbd_function_driver *, char *, void *);
+//struct usbd_function_instance * usbd_alloc_interface_function (struct usbd_function_driver *, char *, void *);
+//struct usbd_function_instance * usbd_alloc_class_function (struct usbd_function_driver *function_driver, char*, void *);
+//struct usbd_function_instance * usbd_alloc_composite_function (struct usbd_function_driver *function_driver, char*, char*,
+// char **, void *);
+
+//void usbd_dealloc_function (struct usbd_function_instance *);
+//void usbd_dealloc_interface (struct usbd_function_instance *);
+//void usbd_dealloc_class (struct usbd_function_instance *);
+
+/*! @} */
+
+
+/*!
+ * @name String Descriptor database
+ *
+ * @{
+ */
+
+struct usbd_string_descriptor *usbd_get_string_descriptor (struct usbd_function_instance *, u8);
+u8 usbd_realloc_string (struct usbd_function_instance *, u8, const char *);
+u8 usbd_alloc_string (struct usbd_function_instance *, const char *);
+//void usbd_free_string_descriptor(struct usbd_function_instance *, u8 );
+
+
+
+//extern struct usbd_string_descriptor **usb_strings;
+//void usbd_free_descriptor_strings (struct usbd_descriptor *);
+
+/*! @} */
+
+/*!
+ * @name LANGID's
+ *
+ * @{
+ */
+#define LANGID_ENGLISH "\011"
+#define LANGID_US_ENGLISH "\004"
+#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH
+/*! @} */
+
+
+/*!
+ * @name Well Known string indices
+ *
+ * Used by function drivers to set certain strings
+ * @{
+ */
+#define STRINDEX_LANGID 0
+#define STRINDEX_IPADDR 1
+#define STRINDEX_PRODUCT 2
+/*! @} */
+
+
+
+
+/*!
+ * @name Device Information
+ *
+ * @{
+ */
+BOOL usbd_high_speed(struct usbd_function_instance *instance);
+int usbd_bmaxpower(struct usbd_function_instance *instance);
+int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *instance, int index, BOOL hs);
+int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *instance, BOOL hs);
+int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *instance, int index, BOOL hs);
+int usbd_get_bMaxPower(struct usbd_function_instance *);
+/*! @} */
+
+
+/*!
+ * @name Device Control
+ *
+ * @{
+ */
+usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *);
+usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *);
+#if 0
+int usbd_framenum(struct usbd_function_instance *);
+otg_tick_t usbd_ticks(struct usbd_function_instance *);
+otg_tick_t usbd_elapsed(struct usbd_function_instance *, otg_tick_t *, otg_tick_t *);
+#endif
+/*! @} */
+
+/*!
+ * @name Endpoint I/O
+ *
+ * @{
+ */
+
+//typedef int usbd_urb_notification(struct usbd_urb *urb, int rc);
+struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *, int endpoint_index, int length, usbd_urb_notification *notify);
+struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *, int length, usbd_urb_notification *notify);
+void usbd_free_urb (struct usbd_urb *urb);
+int usbd_start_in_urb (struct usbd_urb *urb);
+int usbd_start_out_urb (struct usbd_urb *);
+int usbd_cancel_urb(struct usbd_urb *);
+int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index);
+int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint);
+
+/*!
+ * @brief _usbd_unlink_urb() - return first urb in list
+ *
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ * Called from interrupt.
+ *
+ * @param urb linked list of urbs
+ * @return pointer to urb
+ */
+static void INLINE usbd_unlink_urb(struct usbd_urb *urb)
+{
+ urb_link *ul = &urb->link;
+ ul->next->prev = ul->prev;
+ ul->prev->next = ul->next;
+ ul->prev = ul->next = ul;
+}
+
+
+
+/*! @} */
+
+
+/*!
+ * @name endpoint information
+ *
+ * Used by function drivers to get specific endpoint information
+ * @{
+ */
+
+int usbd_endpoint_transferSize(struct usbd_function_instance *, int, int);
+int usbd_endpoint_interface(struct usbd_function_instance *, int);
+int usbd_interface_AltSetting(struct usbd_function_instance *, int);
+int usbd_ConfigurationValue(struct usbd_function_instance *);
+void usbd_endpoint_update(struct usbd_function_instance *, int , struct usbd_endpoint_descriptor *, int);
+/*! @} */
+
+//int usbd_remote_wakeup_enabled(struct usbd_function_instance *);
+
+/*!
+ * @name device information
+ *
+ * Used by function drivers to get device feature information
+ * @{
+ */
+int usbd_otg_bmattributes(struct usbd_function_instance *);
+/*! @} */
+
+/*!
+ * @name usbd_feature_enabled - return non-zero if specified feature has been enabled
+ * @{
+ */
+int usbd_feature_enabled(struct usbd_function_instance *function, int f);
+/*! @} */
+
+/* DEPRECATED */
+/*!
+ * @name Access to function privdata (DEPRECATED)
+ * @{
+ */
+void *usbd_function_get_privdata(struct usbd_function_instance *function);
+void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata);
+void usbd_flush_endpoint_index (struct usbd_function_instance *, int );
+int usbd_endpoint_urb_num (struct usbd_function_instance *function, int endpoint_index);
+int usbd_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int max, int descriptor_type, int index);
+/*! @} */
+
+#endif /* USBP_FUNC_H */
diff --git a/drivers/otg/otg/usbp-hid.h b/drivers/otg/otg/usbp-hid.h
new file mode 100644
index 000000000000..c1aeff599f2f
--- /dev/null
+++ b/drivers/otg/otg/usbp-hid.h
@@ -0,0 +1,98 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-hid.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-hid.h|20070810225414|35914
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+/*!
+ * @file otg/otg/usbp-hid.h
+ * @brief HID class descriptor structure definitions.
+ *
+ * @ingroup USBDAPI
+ */
+
+/*!
+ * @name HID requests
+ */
+ /*! @{ */
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_GET_IDLE 0x02
+#define USB_REQ_GET_PROTOCOL 0x03
+#define USB_REQ_SET_REPORT 0x09
+#define USB_REQ_SET_IDLE 0x0A
+#define USB_REQ_SET_PROTOCOL 0x0B
+/*! @} */
+
+
+/*!
+ * @name HID - Class Descriptors
+ * C.f. 7.1.1
+ */
+ /*! @{ */
+
+#define HID_DT_HID 0x21
+#define HID_DT_REPORT 0x22
+#define HID_DT_PHYSICAL 0x23
+
+/*! @} */
+
+/*!
+ * @name HID - Report Types
+ * C.f. 7.1.1
+ */
+ /*! @{ */
+
+#define HID_INPUT 0x01
+#define HID_OUTPUT 0x02
+#define HID_FEATURE 0x03
+
+/*! @} */
+
+/*!
+ * @name HID Descriptor
+ * C.f. E.8
+ */
+ /*! @{ */
+
+/*! @struct hid_descriptor usbp-hid.h "otg/usbp-hid.h"
+ *
+ * @brief hid class descriptor struct
+ */
+PACKED0 struct PACKED1 hid_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bcdHID;
+ u8 bCountryCode;
+ u8 bNumDescriptors;
+ u8 bReportType;
+ u16 wItemLength;
+};
+
+/*! @} */
+
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
diff --git a/drivers/otg/otg/usbp-hub.h b/drivers/otg/otg/usbp-hub.h
new file mode 100644
index 000000000000..87b54089d22d
--- /dev/null
+++ b/drivers/otg/otg/usbp-hub.h
@@ -0,0 +1,126 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-hub.h
+ * @(#) balden@belcarra.com|otg/otg/usbp-hub.h|20070326205353|47221
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otg/usbp-hub.h
+ * @brief USB Hub class descriptor structure definitions.
+ *
+ * @ingroup USBDAPI
+ */
+
+
+/*!
+ * @name Hub requests
+ */
+ /*! @{ */
+#define USB_REQ_CLEAR_TT_BUFFER 0x08
+#define USB_REQ_RESET_TT 0x09
+#define USB_REQ_GET_TT_STATE 0x0A
+#define USB_REQ_STOP_TT 0x0B
+/*! @} */
+
+/*!
+ * @name Hub Recipients
+ */
+ /*! @{ */
+#define USB_REQ_RECIPIENT_HUB 0x00
+#define USB_REQ_RECIPIENT_PORT 0x03
+/*! @} */
+
+/*!
+ * @name Hub Feature Selectors C.f. Table 11-17
+ *
+ *
+ */
+ /*! @{ */
+#if !defined(C_HUB_LOCAL_POWER)
+#define C_HUB_LOCAL_POWER 0x0
+#endif
+#if !defined(C_HUB_OVER_CURRENT)
+#define C_HUB_OVER_CURRENT 0x1
+#endif
+
+#define PORT_CONNECTION 0 // 0x01
+#define PORT_ENABLE 1 // 0x02
+#define PORT_SUSPEND 2 // 0x04
+#define PORT_OVER_CURRENT 3 // 0x08
+#define PORT_RESET 4 // 0x10
+
+#define PORT_POWER 8 // 0x100
+#define PORT_LOW_SPEED 9 // 0x200
+#define PORT_HIGH_SPEED 10 // 0x400
+
+#define C_PORT_CONNECTION 16
+#define C_PORT_ENABLE 17
+#define C_PORT_SUSPEND 18
+#define C_PORT_OVER_CURRENT 19
+#define C_PORT_RESET 20
+
+#define PORT_TEST 21
+#define PORT_INDICATOR 22
+
+/*! @} */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+
+
+/*!
+ * @name Hub Descriptor C.f. 11.23.2.1 Table 11-13
+ * N.B. This assumes 1-8 ports, DeviceRemovable and PortPwrCtrlMask
+ * must be sized for the number of if ports > 8.
+ */
+/*! @struct hub_descriptor usbp-hub.h "otg/usbp-hub.h"
+ */
+PACKED0 struct PACKED1 hub_descriptor {
+ u8 bDescLength;
+ u8 bDescriptorType;
+ u8 bNbrPorts;
+ u16 wHubCharacteristics;
+ u8 bPwrOn2PwrGood;
+ u8 bHubContrCurrent;
+ u8 DeviceRemovable;
+ u8 PortPwrCtrlMask;
+};
+/*! @name HUB status defines
+ @{ */
+
+#define HUB_GANGED_POWER 0x00
+#define HUB_INDIVIDUAL_POWER 0x01
+#define HUB_COMPOUND_DEVICE 0x04
+#define HUB_GLOBAL_OVERCURRENT 0x00
+#define HUB_INDIVIDUAL_OVERCURRENT 0x08
+#define HUB_NO_OVERCURRENT 0x10
+#define HUB_TT_8 0x00
+#define HUB_TT_16 0x20
+#define HUB_TT_24 0x40
+#define HUB_TT_32 0x60
+#define HUB_INDICATORS_SUPPORTED 0x80
+/*! @} */
+
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
diff --git a/drivers/otg/otg/usbp-mcpc.h b/drivers/otg/otg/usbp-mcpc.h
new file mode 100644
index 000000000000..2b7437a53102
--- /dev/null
+++ b/drivers/otg/otg/usbp-mcpc.h
@@ -0,0 +1,217 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-mcpc.h
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-mcpc.h|20061218212925|13063
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+
+/*!
+ * @file otg/otg/usbp-mcpc.h
+ * @brief MCPC class descriptor structure definitions.
+ *
+ * @ingroup USBDCORE
+ */
+
+#ifdef PRAGMAPACK
+#pragma pack(push,1)
+#endif
+
+
+/*!
+ * @name Management Element Requests
+ * C.f. Table 11 - bRequest Value
+ */
+ /*! @{ */
+#define MCPC_REQ_ACTIVATE_MODE 0x60
+#define MCPC_REQ_GET_MODETABLE 0x61
+#define MCPC_REQ_SET_LINK 0x62
+#define MCPC_REQ_CLEAR_LINK 0x63
+#define MCPC_REQ_MODE_SPECIFIC_REQUEST 0x64
+ /*! @} */
+
+
+/*!
+ * @name MCPC Vendor Requests
+ * C.f.
+ */
+ /*! @{ */
+#define MCPC_VENDOR_REQUEST 0x44
+#define MCPC_VENDOR_SUBREQUEST_MACM_AB 0x11
+#define MCPC_VENDOR_SUBREQUEST_MACM_DL 0x12
+ /*! @} */
+
+
+
+/*!
+* @name C.f. Table 16 - Connection Model selector
+ */
+ /*! @{ */
+
+#define MCPC_MOBILE_ASTRACT_CONTROL_MODEL 0x00
+#define MCPC_MOBILE_DIRECT_LINE_MODEL 0x01
+ /*! @} */
+
+
+/*!
+ * @name Notification codes
+ * C.f. Table 20 bNotification value
+ */
+ /*! @{ */
+#define MCPC_REQUEST_MODE 0x30
+#define MCPC_REQUEST_ACKNOWLEDGE 0x31
+#define MCPC_MODE_SPECIFIC_NOTIFICATION 0x30
+ /*! @} */
+
+/*!
+ * @name c.f. MCPC Table 20
+ */
+ /*! @{ */
+
+#define MCPC_NOTIFICATION_REQUEST_MODE 0x30
+#define MCPC_NOTIFICATION_REQUEST_ACKNOWLEDGE 0x31
+#define MCPC_NOTIFICATION_MODE_SPECIFIC 0x32
+
+#define MCPC_MODE_SELECTOR_OK 0x00
+#define MCPC_MODE_SELECTOR_NG 0x01
+
+/*! @} */
+
+
+/*!
+ * @name C.f. Table 25 Mobile Abstract Control Model Specific Function Descriptor
+ */
+ /*! @{ */
+/*! @struct mpcp_abstract_control_model_specific_functional_descriptor usbp-mcpc.h "otg/usbp-mcpc.h"
+ */
+struct mpcp_abstract_control_model_specific_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bType;
+ u8 bMode_n[1];
+};
+
+ /*! @} */
+
+/*!
+ * @name bType pipe groups
+ * C.f. MCPC Table 25
+ * C.f. MCPC Table 26
+ */
+#define MCPC_PIPE_GROUP_AB_1 0x01
+#define MCPC_PIPE_GROUP_AB_2 0x02
+#define MCPC_PIPE_GROUP_AB_3 0x03
+#define MCPC_PIPE_GROUP_AB_4 0x04
+#define MCPC_PIPE_GROUP_AB_5 0x05
+#define MCPC_PIPE_GROUP_AB_6 0x06
+#define MCPC_PIPE_GROUP_DL_1 0x01
+#define MCPC_PIPE_GROUP_DL_2 0x02
+/*! @} */
+
+
+/*!
+ * @name C.f. Table 26 Mobile Direct Line Model Specific Function Descriptor
+ */
+ /*! @{ */
+/*! @struct mpcp_direct_line_model_specific_functional_descriptor usbp-mcpc.h "otg/usbp-mcpc.h"
+ */
+struct mpcp_direct_line_model_specific_functional_descriptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bType;
+ u8 bMode_n[1];
+};
+
+#define MCPC_PIPE_GROUP_DL_1 0x01
+#define MCPC_PIPE_GROUP_DL_2 0x02
+ /*! @} */
+
+/*!
+ * @name C.f. Table 27 bDescriptorSubType in Functional Descriptor
+ */
+ /*! @{ */
+#define MCPC_MACM_FUNCTIONAL_DESCRIPTOR 0x11
+#define MCPC_MDLM_FUNCTIONAL_DESCRIPTOR 0x12
+ /*! @} */
+
+
+/*!
+ * @name C.f. Table 28 Mode (bMode_x) Value
+ */
+ /*! @{ */
+
+#define MCPC_MODE_DEACTIVATE 0x00
+#define MCPC_MODE_MODEM_MODE 0x01
+#define MCPC_MODE_AT_COMMAND_CONTROL_MODE 0x02
+#define MCPC_MODE_LAN_MODE 0x03
+#define MCPC_MODE_VOICE_COMMUNICATION_MODE 0x30
+#define MCPC_MODE_OBJECT_EXCHANGE_MODE 0x60
+#define MCPC_MODE_MDLM_FULL_SUPPORT 0x90
+#define MCPC_MODE_MDLM_ASYNC_ONLY 0x91
+#define MCPC_MODE_UNLINKED_STATE 0xff
+ /*! @} */
+
+/*!
+ * @name MCPC
+ */
+ /*! @{ */
+/*! @struct usbd_mobile_abstract_control_model_specific_desciptor usbp-mcpc.h "otg/usbp-mcpc.h"
+ */
+
+PACKED0 struct PACKED1 usbd_mobile_abstract_control_model_specific_desciptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubtype;
+ u8 bType;
+ u8 bMode_N[4];
+};
+/*! @struct usbd_mobile_direct_line_model_specific_desciptor usbp-mcpc.h "otg/usbp-mcpc.h"
+ */
+PACKED0 struct PACKED1 usbd_mobile_direct_line_model_specific_desciptor {
+ u8 bFunctionLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bType;
+ u8 bMode_N[4];
+};
+
+
+
+/*! @var typedef enum mcpc_mode mcpc_mode_t
+ *
+ * @brief MCPC mode
+ */
+typedef enum mcpc_mode {
+ mcpc_unlinked,
+ mcpc_linked,
+ mcpc_activated
+} mcpc_mode_t;
+
+
+/*! @} */
+
+#ifdef PRAGMAPACK
+#pragma pack(pop)
+#endif
+
+
+/* End of FILE */
diff --git a/drivers/otg/otg/usbp-pcd.h b/drivers/otg/otg/usbp-pcd.h
new file mode 100644
index 000000000000..94f767797e14
--- /dev/null
+++ b/drivers/otg/otg/usbp-pcd.h
@@ -0,0 +1,296 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/usbp-pcd.h - OTG Peripheral Controller Driver
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otg/usbp-pcd.h|20070820053712|09860
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/otg/usbp-pcd.h
+ * @brief Peripheral Controller Driver Related Structures and Definitions.
+ *
+ * @ingroup PCD
+ */
+
+/*!
+ * The pcd_ops structure contains pointers to all of the functions implemented for the
+ * linked in driver. Having them in this structure allows us to easily determine what
+ * functions are available without resorting to ugly compile time macros or ifdefs
+ *
+ * There is only one instance of this, defined in the device specific lower layer.
+ */
+struct usbd_pcd_ops {
+
+ /* mandatory */
+ u8 bmAttributes;
+ int max_endpoints;
+ int ep0_packetsize;
+ u8 high_speed_capable;
+ u32 capabilities; /* UDC Capabilities - see usbd-bus.h for details */
+ u8 bMaxPower;
+ char *name;
+ u8 ports;
+
+
+ /* 3. called by ot_init() if defined */
+ int (* serial_init) (struct pcd_instance *); /* get device serial number if available */
+
+ /* 4. called from usbd_enable_function_irq() - REQUIRED */
+ int (*request_endpoints) /* process endpoint_request list and return endpoint_map */
+ (struct pcd_instance *,
+ struct usbd_endpoint_map *, /* showing allocated endpoints with attributes, addresses */
+ int, struct usbd_endpoint_request*); /* and other associated information */
+
+ /* 5. called from usbd_enable_function_irq() - REQUIRED */
+ int (*set_endpoints) /* setup required endpoints in hardware */
+ (struct pcd_instance *, int , struct usbd_endpoint_map *);
+
+ /* 6. called by xxx() if defined */
+ void (*enable) (struct pcd_instance *); /* enable the UDC */
+
+ /* 7. called by xxx() if defined */
+ void (*disable) (struct pcd_instance *); /* disable the UDC */
+
+ /* 8. called by xxx() if defined */
+ void (*start) (struct pcd_instance *); /* called for DEVICE_CREATE to start the UDC */
+ void (*stop) (struct pcd_instance *); /* called for DEVICE_DESTROY to stop the UDC */
+ void (*disable_ep)
+ (struct pcd_instance *,
+ unsigned int ep, /* called for DEVICE_DESTROTY to disable endpoint zero */
+ struct usbd_endpoint_instance *endpoint);
+
+ /* 9. called by xxx() if defined */
+ void (*set_address)
+ (struct pcd_instance *,
+ unsigned char address); /* called for DEVICE_RESET and DEVICE_ADDRESS_ASSIGNED to */
+
+
+ /* 10. called by xxx() if defined */
+ void (*setup_ep)
+ (struct pcd_instance *,
+ unsigned int ep, /* called for DEVICE_CONFIGURED to setup specified endpoint for use */
+ struct usbd_endpoint_instance *endpoint);
+
+ /* 11. */
+ void * (*alloc_buffer)(struct pcd_instance*, int);
+ void (*free_buffer)(struct pcd_instance*, void *);
+
+ /* 12. called by xxx() if defined */
+ void (*reset_ep)
+ (struct pcd_instance *,
+ unsigned int ep); /* called for DEVICE_RESET to reset endpoint zero */
+
+
+ /* 13. called by usbd_send_urb() - REQUIRED */
+ void (*start_endpoint_in) /* start an IN urb */
+ (struct pcd_instance *,
+ struct usbd_endpoint_instance *);
+
+ /* 14. called by usbd_start_recv() - REQUIRED */
+ void (*start_endpoint_out) /* start an OUT urb */
+ (struct pcd_instance *,
+ struct usbd_endpoint_instance *);
+
+ /* 15. called by usbd_cancel_urb_irq() */
+ void (*cancel_in_irq)
+ (struct pcd_instance *,
+ struct usbd_urb *urb); /* cancel active urb for IN endpoint */
+ void (*cancel_out_irq)
+ (struct pcd_instance *,
+ struct usbd_urb *urb); /* cancel active urb for OUT endpoint */
+
+
+ /* 16. called from ep0_recv_setup() for GET_STATUS request */
+ int (*endpoint_halted)
+ (struct pcd_instance *,
+ struct usbd_endpoint_instance *); /* Return non-zero if requested endpoint is halted */
+ int (*halt_endpoint)
+ (struct pcd_instance *,
+ struct usbd_endpoint_instance *,
+ int); /* Return non-zero if requested endpoint clear fails */
+
+ int (*device_feature)
+ (struct pcd_instance *,
+ int,
+ int); /* Return non-zero if requested endpoint clear fails */
+
+ int (*remote_wakeup)
+ (struct pcd_instance *); /* Return non-zero if remote wakeup fails */
+
+ /* 17 */
+ void (*startup_events) (struct pcd_instance *); /* perform UDC specific USB events */
+
+ /* 18. root hub operations
+ */
+ int (*hub_status) (struct pcd_instance *);
+ int (*port_status) (struct pcd_instance *, int);
+ int (*hub_feature) (struct pcd_instance *, int, int);
+ int (*port_feature) (struct pcd_instance *, int, int, int);
+ int (*get_ports_status) (struct pcd_instance *);
+ int (*wait_for_change) (struct pcd_instance *);
+
+ /* 19. configuration changes
+ */
+ int (*vbus_status) (struct pcd_instance *);
+ int (*softcon) (struct pcd_instance *, int);
+ u16 (*framenum) (struct pcd_instance *);
+
+ otg_tick_t (*ticks)(struct pcd_instance *);
+ otg_tick_t (*elapsed)(otg_tick_t *, otg_tick_t *);
+
+};
+
+extern struct usbd_pcd_ops usbd_pcd_ops;
+
+/*! bus_set_speed
+ */
+void bus_set_speed(struct usbd_bus_instance *bus, int hs);
+
+
+
+/*! pcd_rcv_complete_irq - complete a receive operation
+ * Called by UDC driver to show completion of outstanding I/O, this will
+ * update the urb length and if necessary will finish the urb passing it to the
+ * bottom half which will in turn call the function driver callback.
+ */
+struct usbd_urb * pcd_rcv_complete_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad);
+struct usbd_urb * pcd_rcv_finished_irq (struct usbd_endpoint_instance *endpoint, int len, int urb_bad);
+void pcd_rcv_cancelled_irq (struct usbd_endpoint_instance *endpoint);
+struct usbd_urb * pcd_tx_next_irq (struct usbd_endpoint_instance *endpoint);
+struct usbd_urb * pcd_rcv_next_irq (struct usbd_endpoint_instance *endpoint);
+void pcd_urb_finished_irq(struct usbd_urb *urb, int rc);
+
+
+/*!
+ * pcd_configure_device is used by function drivers (usually the control endpoint)
+ * to change the device configuration.
+ *
+ * usbd_device_event is used by bus interface drivers to tell the higher layers that
+ * certain events have taken place.
+ */
+//void pcd_bus_event_handler (struct usbd_bus_instance *, usbd_device_event_t, int);
+void pcd_bus_event_handler_irq (struct usbd_bus_instance *, usbd_device_event_t, int);
+
+
+
+/*! pcd_tx_complete_irq - complete a transmit operation
+ * Called by UDC driver to show completion of an outstanding I/O, this will update the urb sent
+ * information and if necessary will finish the urb passing it to the bottom half which will in
+ * turn call the function driver callback.
+ */
+struct usbd_urb * pcd_tx_complete_irq (struct usbd_endpoint_instance *endpoint, int restart);
+
+
+/*! pcd_tx_cancelled_irq - finish pending tx_urb with USBD_URB_CANCELLED
+ * Called by UDC driver to cancel the current transmit urb.
+ */
+static __inline__ void pcd_tx_cancelled_irq (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *tx_urb;
+ struct usbd_bus_instance *bus;
+ struct pcd_instance *pcd;
+ RETURN_IF (! (tx_urb = endpoint->tx_urb));
+ bus = tx_urb->bus;
+ pcd = (struct pcd_instance *)bus->privdata;
+ TRACE_MSG1(pcd->TAG, "BUS_TX CANCELLED: %p", (int) endpoint->tx_urb);
+ pcd_urb_finished_irq (tx_urb, USBD_URB_CANCELLED);
+ endpoint->sent = endpoint->last = 0;
+ endpoint->tx_urb = NULL;
+}
+
+
+/*! pcd_tx_sendzlp - test if we need to send a ZLP
+ * This has a side-effect of reseting the USBD_URB_SENDZLP flag.
+ */
+static __inline__ int pcd_tx_sendzlp (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *tx_urb = endpoint->tx_urb;
+ struct usbd_bus_instance *bus;
+ struct pcd_instance *pcd;
+
+ RETURN_FALSE_UNLESS(tx_urb); // no urb
+
+ bus = tx_urb->bus;
+ pcd = (struct pcd_instance *)bus->privdata;
+ TRACE_MSG3(pcd->TAG, "sent: %d actual: %d flags: %02x", endpoint->sent, tx_urb->actual_length, tx_urb->flags);
+
+ RETURN_FALSE_IF(!endpoint->sent && tx_urb->actual_length); // nothing sent yet and there is data to send
+ RETURN_FALSE_IF(tx_urb->actual_length > endpoint->sent); // still data to send
+ RETURN_FALSE_UNLESS(tx_urb->flags & USBD_URB_SENDZLP); // flag not set
+
+ TRACE_MSG0(pcd->TAG, "SENDZLP");
+ tx_urb->flags &= ~USBD_URB_SENDZLP;
+ return TRUE;
+}
+
+
+/*! pcd_rcv_complete_irq - complete a receive
+ * Called from rcv interrupt to complete.
+ */
+static __inline__ void pcd_rcv_fast_complete_irq (struct usbd_endpoint_instance *endpoint, struct usbd_urb *rcv_urb)
+{
+ //TRACE_MSG1(PCD, "BUS_RCV FAST COMPLETE: %d", rcv_urb->actual_length);
+ pcd_urb_finished_irq (rcv_urb, USBD_URB_OK);
+}
+
+/*! pcd_recv_setup - process a device request
+ * Note that we verify if a receive urb has been queued for H2D with non-zero wLength
+ * and return -EINVAL to stall if the upper layers have not properly tested for and
+ * setup a receive urb in this case.
+ */
+static __inline__ int pcd_recv_setup_irq (struct pcd_instance *pcd, struct usbd_device_request *request)
+{
+ struct usbd_bus_instance *bus = pcd->bus;
+ //struct usbd_endpoint_instance *endpoint = bus->endpoint_array + 0;
+ //TRACE_SETUP (pcd->TAG, request);
+ return usbd_device_request_irq(bus, request); // fail if already failed
+ #if 0
+ RETURN_EINVAL_IF (usbd_device_request_irq(bus, request)); // fail if already failed
+ TRACE_MSG2(pcd->TAG, "BUS_RECV SETUP: RCV URB: %x TX_URB: %x", (int)endpoint->rcv_urb, (int)endpoint->tx_urb);
+ RETURN_ZERO_IF ( (request->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_DEVICE2HOST);
+ RETURN_ZERO_IF (!le16_to_cpu (request->wLength));
+ RETURN_EINVAL_IF (!endpoint->rcv_urb);
+ return 0;
+ #endif
+}
+
+/*! pcd_recv_setup_emulate_irq - emulate a device request
+ * Called by the UDC driver to inject a SETUP request. This is typically used
+ * by drivers for UDC's that do not pass all SETUP requests to us but instead give us
+ * a configuration change interrupt.
+ */
+int pcd_recv_setup_emulate_irq(struct usbd_bus_instance *, u8, u8, u16, u16, u16);
+
+/*! pcd_ep0_reset_irq - reset ep0 endpoint
+ */
+static void __inline__ pcd_ep0_reset_endpoint_irq (struct usbd_endpoint_instance *endpoint)
+{
+ //TRACE_MSG0(PCD, "BUS_EP0 RESET");
+ pcd_tx_cancelled_irq (endpoint);
+ pcd_rcv_cancelled_irq (endpoint);
+ endpoint->sent = endpoint->last = 0;
+}
+
+/*! pcd_check_device_feature - verify that feature is set or clear
+ * Check current feature setting and emulate SETUP Request to set or clear
+ * if required.
+ */
+void pcd_check_device_feature(struct usbd_bus_instance *, int , int );
diff --git a/drivers/otg/otgcore/Makefile b/drivers/otg/otgcore/Makefile
new file mode 100644
index 000000000000..31348c6b3b1d
--- /dev/null
+++ b/drivers/otg/otgcore/Makefile
@@ -0,0 +1,50 @@
+#
+# Belcarra OTG - On-The-Go
+# @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/Makefile-l26|20061213004244|25170
+#
+# Copyright (c) 2004 Belcarra Technologies Corp
+# Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+# Makefile for Linux 2.6.x systems
+
+OTG=$(TOPDIR)/drivers/otg
+EXTRA_CFLAGS += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+EXTRA_CFLAGS_nostdinc += -Wno-missing-prototypes -Wno-unused -Wno-format -I$(OTG)
+
+otgcore-objs := core-init-lnx.o otg.o \
+ otg-trace.o otg-trace-lnx.o \
+ otg-mesg.o otg-mesg-lnx.o \
+ otg-fw.o usbp-bops.o usbp-fops.o usbp-procfs.o
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL),y)
+otgcore-objs += otg-fw-mn.o
+endif
+
+ifeq ($(CONFIG_OTG_USB_HOST),y)
+otgcore-objs += otg-fw-mn.o
+endif
+
+ifeq ($(CONFIG_OTG_USB_PERIPHERAL_OR_HOST),y)
+otgcore-objs += otg-fw-mn.o
+endif
+
+ifeq ($(CONFIG_OTG_BDEVICE_WITH_SRP),y)
+otgcore-objs += otg-fw-df.o
+endif
+
+ifeq ($(CONFIG_OTG_DEVICE),y)
+otgcore-objs += otg-fw-df.o
+endif
+
+
+#ifeq ("$(CONFIG_OTG_TRACE)", "y")
+#otgcore-objs += usbp-procfs.o
+#endif
+
+#usbprocfs-objs := usbp-procfs.o
+
+obj-$(CONFIG_OTG) += otgcore.o
+
+#obj-$(CONFIG_OTG_PROCFSM) += usbprocfs.o
+
+#otgtrace-objs := otg-trace.o
+#obj-$(CONFIG_OTG_TRACE) += otgtrace.o
diff --git a/drivers/otg/otgcore/core-init-lnx.c b/drivers/otg/otgcore/core-init-lnx.c
new file mode 100644
index 000000000000..d85860f0945f
--- /dev/null
+++ b/drivers/otg/otgcore/core-init-lnx.c
@@ -0,0 +1,313 @@
+/*
+ * 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
+ */
+/*
+ * otg/otg/core-init-l24.c - OTG Peripheral Controller Driver Module Initialization
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/otgcore/core-init-lnx.c|20070808203850|03038
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ * This is the linux 2.4 version.
+ *
+ */
+/*!
+ * @file otg/otgcore/core-init-lnx.c
+ * @brief OTG Core Linux initialization.
+ *
+ * This file is the starting point for defining the Linux OTG Core
+ * driver. It references and starts all of the other components that
+ * must be "linked" into the OTGCORE mdoule.
+ *
+ * @ingroup LINUXOS
+ */
+
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-ocd.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
+#include <linux/platform_device.h>
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12) */
+
+EMBED_LICENSE(); // When supported by the OS, embed license information in the binary
+
+
+#ifdef OTG_MALLOC_TEST
+int otg_mallocs;
+#endif /* OTG_MALLOC_TEST */
+
+otg_tag_t CORE;
+//int usbd_procfs_init (void);
+//void usbd_procfs_exit (void);
+
+
+/* Tables for OTG firmware
+ */
+#if defined(CONFIG_OTG_USB_PERIPHERAL) || defined (CONFIG_OTG_USB_HOST) || defined(CONFIG_OTG_USB_PERIPHERAL_OR_HOST)
+struct otg_firmware *otg_firmware_loaded = &otg_firmware_mn;
+struct otg_firmware *otg_firmware_orig = &otg_firmware_mn;
+#elif defined(CONFIG_OTG_BDEVICE_WITH_SRP) || defined(CONFIG_OTG_DEVICE)
+struct otg_firmware *otg_firmware_loaded = &otg_firmware_df;
+struct otg_firmware *otg_firmware_orig = &otg_firmware_df;
+#else /* error */
+//#abort "Missing USB or OTG configuration"
+#endif
+
+
+struct otg_firmware *otg_firmware_loading;
+
+
+//struct otg_instance otg_instance_private = {
+//};
+
+
+/*! otg_get_state_name - get corresponding name of specific state value
+ *
+ * @param state - state value
+ * @return state name
+ */
+char * otg_get_state_name(int state)
+{
+ struct otg_state *otg_state;
+ if (!otg_firmware_loaded || (state >= otg_firmware_loaded->number_of_states))
+ return "UNKNOWN_STATE";
+
+ otg_state = otg_firmware_loaded->otg_states + state;
+ return otg_state->name;
+}
+
+
+/* ************************************************************************************* */
+
+
+
+
+#if defined (CONFIG_OTG_USBD_PM)
+int usbd_load(char * arg) { return 0; }
+int usbd_unload(char *arg) { return 0; }
+OTG_EXPORT_SYMBOL(usbd_load);
+OTG_EXPORT_SYMBOL(usbd_unload);
+#endif
+/*! otg_create -create an otg instance
+ *
+ * @return created otg intance pointer
+ */
+struct otg_instance *otg_create(void)
+{
+ int message_init = 0;
+ int message_init_l24 = 0;
+ //int trace_init = 0;
+ //int trace_init_l24 = 0;
+ struct otg_instance *otg = NULL;
+
+
+ THROW_UNLESS((otg = CKMALLOC(sizeof(struct otg_instance))), error);
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //trace_init = otg_trace_init();
+ //trace_init_l24 = otg_trace_init_l24();
+ CORE = otg_trace_obtain_tag(NULL, "core-otg");
+ otg->TAG = CORE;
+
+ otg->function_name[0] = '\0';
+
+ TRACE_MSG0(CORE,"--");
+ otg->current_outputs = otg_firmware_loaded->otg_states;
+ otg->previous_outputs = otg_firmware_loaded->otg_states;
+
+ /* initialize otg-mesg and usbp
+ */
+ THROW_IF((message_init = otg_message_init(otg)), error);
+ THROW_IF((message_init_l24 = otg_message_init_l24(otg)), error);
+ THROW_IF(usbd_device_init(), error);
+
+
+ otg_set_ocd_ops(otg, NULL);
+ otg_set_tcd_ops(otg, NULL);
+ otg_set_hcd_ops(otg, NULL);
+ otg_set_pcd_ops(otg, NULL);
+
+ return otg;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (message_init_l24 )otg_message_exit_l24(otg);
+ if (message_init) otg_message_exit();
+ CORE = otg_trace_invalidate_tag(CORE);
+ if (otg) LKFREE(otg);
+ return NULL;
+ }
+}
+/*! otg_destroy - free otg instance and associated resources
+ * @param otg - otg instance pointer
+ * @return none
+ */
+void otg_destroy(struct otg_instance *otg)
+{
+ //usbd_procfs_exit ();
+ usbd_device_exit();
+ otg_message_exit_l24(otg);
+ otg_message_exit();
+
+
+ if (otg_firmware_loading) {
+ lkfree(otg_firmware_loading->otg_states);
+ lkfree(otg_firmware_loading->otg_tests);
+ lkfree(otg_firmware_loading);
+ }
+ if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) {
+ lkfree(otg_firmware_loaded->otg_states);
+ lkfree(otg_firmware_loaded->otg_tests);
+ lkfree(otg_firmware_loaded);
+ }
+
+ CORE = otg_trace_invalidate_tag(CORE);
+ otg->TAG = NULL;
+
+//#if defined(LINUX24)
+// otg_trace_exit_l24();
+//#endif
+ //otg_trace_exit();
+
+ LKFREE(otg);
+}
+
+OTG_EXPORT_SYMBOL(otg_create);
+OTG_EXPORT_SYMBOL(otg_destroy);
+
+/* ************************************************************************************* */
+int otg_trace_modinit_lnx(void);
+void otg_trace_modexit_lnx(void);
+int usbd_device_modinit(void);
+int usbd_device_modexit(void);
+
+
+/* ************************************************************************************* */
+//#if defined(LINUX26)
+/*! otg_match - check if the device and driver match
+ *
+ * @param dev - pointer to device
+ * @param drv - pointer device driver
+ * @return int for match result
+ */
+
+static int otg_match (struct device *dev, struct device_driver *drv)
+{
+ printk(KERN_INFO"%s:\n", __FUNCTION__);
+ return 0;
+}
+
+void otg_unregister(void)
+{
+ printk(KERN_INFO"%s: \n", __FUNCTION__);
+ usbd_device_modexit();
+ otg_trace_modexit_lnx();
+ printk(KERN_INFO"%s: otg_mallocs: %d\n", __FUNCTION__, otg_mallocs);
+}
+int otg_register(void)
+{
+ int bus_registered = 0;
+ int driver_registered = 0;
+
+ RETURN_EINVAL_IF(otg_trace_modinit_lnx());
+
+ THROW_IF(usbd_device_modinit(), error);
+
+
+ CATCH(error) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* ************************************************************************************* */
+extern u64 otg_events[64];
+extern otg_tag_t otg_tags[64];
+extern char * otg_msgs[64];
+extern u8 otg_head, otg_tail;
+
+//#endif
+
+/*! otg_modinit - linux module initialization
+ */
+static int otg_modinit (void)
+{
+ otg_led_init(LED1);
+ otg_led_init(LED2);
+ return otg_register();
+}
+module_init (otg_modinit);
+
+
+#if OTG_EPILOGUE /* Set nonzero in <otg-module.h> when -DMODULE is in force */
+/*! otg_modexit - This is *only* used for drivers compiled and used as a module.
+ */
+static void otg_modexit (void)
+{
+ otg_unregister();
+}
+#endif
+
+
+
+#ifdef OTG_SKYE_LED
+void otg_led(int led, int flag)
+{
+ if (led)
+ mxc_set_gpio_dataout(led, flag);
+}
+
+void otg_led_init(int led)
+{
+ if (led)
+ mxc_set_gpio_direction(led,0);
+}
+#else /* OTG_SKYE_LED */
+void otg_led(int led, int flag)
+{
+}
+
+void otg_led_init(int led)
+{
+}
+#endif /* OTG_SKYE_LED */
+OTG_EXPORT_SYMBOL(otg_led);
+OTG_EXPORT_SYMBOL(otg_led_init);
+
+
+MOD_EXIT(otg_modexit);
+OTG_EXPORT_SYMBOL(otg_get_state_name);
+
+
+#ifdef OTG_MALLOC_TEST
+OTG_EXPORT_SYMBOL(otg_mallocs);
+#endif /* OTG_MALLOC_TEST */
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/otgcore/otg-fw-df.c b/drivers/otg/otgcore/otg-fw-df.c
new file mode 100644
index 000000000000..a1393b44c001
--- /dev/null
+++ b/drivers/otg/otgcore/otg-fw-df.c
@@ -0,0 +1,137 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-tests-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+
+/*!
+* @file otg/otgcore/otg-fw-df.c
+* @brief OTG Firmware - Firmware for df
+*
+* This file defines the OTG State Machine tests.
+*
+*
+* @ingroup OTGFW
+*/
+
+/*!
+* @page OTGFW
+* @section OTGFW_SECTION - otg-fw-df.c
+* This contains the input, output and timout definitions for the OTG state machine firmware
+*/
+
+
+#ifdef OTG_APPLICATION
+#define NULL 0
+#include "otg-fw.h"
+#include "otg-fw-df.h"
+#else /* OTG_APPLICATION/ */
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+//#include <otg/otg-api.h>
+#include <otg/otg-fw.h>
+#include <otg/otg-fw-df.h>
+#endif /* OTG_APPLICATION */
+
+char otg_fw_name_df[] = "otg_df";
+
+
+struct otg_test otg_tests_df[] = {
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ */
+ /*!
+ * This is the default Firmware. It is included in the
+ * compiled modules and supports the auto Traditional USB
+ * mode. No user inputs are implemented.
+ */
+ { /* */
+ 0, /* .test */
+ invalid_state, /* .state */
+ otg_disabled, /* .target */
+ enable_otg, /* .test1 */
+ },
+ /*
+ * This is not an OTG State. It is used internally to mark the end of the
+ * list of states and inputs.
+ */
+ { /* */
+ 1, /* .test */
+ terminator_state, /* .state */
+ invalid_state, /* .target */
+ 0, /* .test1 */
+ },
+ {2, invalid_state,},
+
+};
+
+#define OTG_TESTS_DF 2
+
+int otg_test_max_df = 2;
+
+ /* eof */
+
+/* Generated by otg-info-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+struct otg_state otg_states_df[OTG_STATES_DF + 1] = {
+ { /* 0 */
+ invalid_state, /* .state */
+ m_otg_init, /* .meta */
+ "invalid_state", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 1 */
+ otg_disabled, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disabled", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 2 */
+ terminator_state, /* .state */
+ m_otg_init, /* .meta */
+ "terminator_state", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ 0,
+ },
+
+ {0, 0, "", 0, 0,},
+
+};
+
+struct otg_firmware otg_firmware_df = {
+ OTG_STATES_DF, /* number of states */
+ OTG_TESTS_DF, /* number of tests */
+ "otg-df", /* name of firmware */
+ otg_states_df, /* struct otg_state * */
+ otg_tests_df, /* struct otg_test * */
+};
+
+/* eof */
diff --git a/drivers/otg/otgcore/otg-fw-mn.c b/drivers/otg/otgcore/otg-fw-mn.c
new file mode 100644
index 000000000000..48abb03c2634
--- /dev/null
+++ b/drivers/otg/otgcore/otg-fw-mn.c
@@ -0,0 +1,928 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-tests-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+
+/*!
+* @file otg/otgcore/otg-fw-mn.c
+* @brief OTG Firmware - Firmware for mn
+*
+* This file defines the OTG State Machine tests.
+*
+*
+* @ingroup OTGFW
+*/
+
+/*!
+* @page OTGFW
+* @section OTGFW_SECTION - otg-fw-mn.c
+* This contains the input, output and timout definitions for the OTG state machine firmware
+*/
+
+
+#ifdef OTG_APPLICATION
+#define NULL 0
+#include "otg-fw.h"
+#include "otg-fw-mn.h"
+#else /* OTG_APPLICATION/ */
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+//#include <otg/otg-api.h>
+#include <otg/otg-fw.h>
+#include <otg/otg-fw-mn.h>
+#endif /* OTG_APPLICATION */
+
+char otg_fw_name_mn[] = "otg_mn";
+
+
+struct otg_test otg_tests_mn[] = {
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ */
+ /*!
+ * This is the initialization set for pcd, hcd and tcd.
+ */
+ /*
+ * This the initial state of the software when first loaded.
+ * It is not possible to return to this state.
+ */
+ { /* Initialize by sending the otg_enable signal. */
+ 0, /* .test */
+ invalid_state, /* .state */
+ otg_disabled, /* .target */
+ enable_otg, /* .test1 */
+ },
+ /*
+ * The USBOTG State Machine has been initialized but is inactive.
+ * This state may have arrived at from either the invalid_state or
+ * from the otg_disable state.
+ */
+ { /* Initialize by sending the otg_enable signal. */
+ 1, /* .test */
+ otg_disabled, /* .state */
+ otg_enable_ocd, /* .target */
+ enable_otg, /* .test1 */
+ },
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+ { /* Wait for ok from de-initializing drivers. */
+ 2, /* .test */
+ otg_disable_tcd, /* .state */
+ otg_disable_hcd, /* .target */
+ TCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+ { /* Wait for ok from de-initializing drivers. */
+ 3, /* .test */
+ otg_disable_hcd, /* .state */
+ otg_disable_pcd, /* .target */
+ HCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+ { /* Wait for ok from de-initializing drivers. */
+ 4, /* .test */
+ otg_disable_pcd, /* .state */
+ otg_disable_ocd, /* .target */
+ PCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine stops the device drivers and waits for them
+ * to signal that they have finished de-initializing.
+ */
+ { /* Wait for ok from de-initializing drivers. */
+ 5, /* .test */
+ otg_disable_ocd, /* .state */
+ otg_disabled, /* .target */
+ OCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+ { /* Wait for ok from initializing drivers. */
+ 6, /* .test */
+ otg_enable_ocd, /* .state */
+ otg_enable_pcd, /* .target */
+ OCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+ { /* Wait for ok from initializing drivers. */
+ 7, /* .test */
+ otg_enable_pcd, /* .state */
+ otg_enable_hcd, /* .target */
+ PCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+ { /* Wait for ok from initializing drivers. */
+ 8, /* .test */
+ otg_enable_hcd, /* .state */
+ otg_enable_tcd, /* .target */
+ HCD_OK, /* .test1 */
+ },
+ /*
+ * The State Machine starts the device drivers and waits for them
+ * to signal that they have finished initializing.
+ */
+ { /* Wait for ok from initializing drivers. */
+ 9, /* .test */
+ otg_enable_tcd, /* .state */
+ otg_enabled, /* .target */
+ TCD_OK, /* .test1 */
+ },
+ /*
+ * Copyright (c) 2005-2007 Belcarra Technologies 2005 Corp
+ *
+ */
+ /*!
+ * This is the minimal firmware. It can be included in the
+ * compiled modules and supports the auto Traditional USB
+ * mode. No user inputs are required for normal operation.
+ *
+ * The b_bus_drop input can be optionally used to disconnect and re-connect.
+ *
+ * The enable_otg input can be optionally used to disable and re-enable.
+ * Note that disable/enable will reset b_bus_drop.
+ *
+ *
+ * B-Device Input Signals
+ *
+ * ID_GND/
+ * B_SESS_VLD
+ *
+ *
+ *
+ * A-Device Input Signals
+ *
+ * ID_GND
+ * DM_HIGH
+ * VBUS_VLD
+ * HUB_PORT_CONNECT
+ * BUS_RESET
+ * ADDRESSED
+ * CONFIGURED
+ * A_SESS_VLD/
+ *
+ *
+ *
+ */
+ /*!
+ * The State Machine has successfully started the device drivers and
+ * is waiting for an input event. Typically it will move from here to
+ * an idle state specific to the current conditions (peripheral_idle etc.)
+ * based on user request b_bus_drop.
+ *
+ */
+ { /* Check for disable. */
+ 10, /* .test */
+ otg_enabled, /* .state */
+ otg_disable_tcd, /* .target */
+ enable_otg_, /* .test1 */
+ Tst_one_second, /* .test2 */
+ },
+ { /* Move to idle. */
+ 11, /* .test */
+ otg_enabled, /* .state */
+ peripheral_idle, /* .target */
+ ID_GND_, /* .test1 */
+ enable_otg, /* .test2 */
+ },
+ { /* */
+ 12, /* .test */
+ otg_enabled, /* .state */
+ host_idle, /* .target */
+ ID_GND, /* .test1 */
+ enable_otg, /* .test2 */
+ },
+ /*!
+ * USB Peripheral is idle.
+ * Waiting for Vbus to indicate that it has been plugged into a USB Host.
+ *
+ */
+ { /* Check for disable (must be done for check for bus_drop.) */
+ 13, /* .test */
+ peripheral_idle, /* .state */
+ otg_enabled, /* .target */
+ ID_GND | enable_otg_, /* .test1 */
+ },
+ { /* Check for b_bus_drop */
+ 14, /* .test */
+ peripheral_idle, /* .state */
+ peripheral_dropped, /* .target */
+ b_bus_drop, /* .test1 */
+ },
+ { /* Move to peripheral mode when SESSION valid. */
+ 15, /* .test */
+ peripheral_idle, /* .state */
+ peripheral_wait, /* .target */
+ B_SESS_VLD, /* .test1 */
+ },
+ /*!
+ * USB Peripheral, user has dropped the bus.
+ */
+ { /* Wait for b_bus_drop/ or enable_otg/ */
+ 16, /* .test */
+ peripheral_dropped, /* .state */
+ peripheral_idle, /* .target */
+ ID_GND | b_bus_drop_ | enable_otg_, /* .test1 */
+ },
+ /*!
+ * USB Peripheral, Vbus sensed, enabling pullup.
+ * The D+ pullup is enabled and we are waiting for a BUS_RESET to
+ * indicate that the USB Host has recognized that a USB Device is attached.
+ */
+ { /* Move to idle if we loose any of these inputs. */
+ 17, /* .test */
+ peripheral_wait, /* .state */
+ peripheral_idle, /* .target */
+ ID_GND | enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */
+ },
+ { /* Move to next state if bus reset is seen. */
+ 18, /* .test */
+ peripheral_wait, /* .state */
+ peripheral_bus_reset, /* .target */
+ BUS_RESET, /* .test1 */
+ },
+ /*!
+ * USB Peripheral, waiting to be addressed.
+ * It is waiting to be enumerated and configured by the USB Host.
+ */
+ { /* Move to idle via discharge, if we loose any of these inputs. */
+ 19, /* .test */
+ peripheral_bus_reset, /* .state */
+ peripheral_discharge_vbus, /* .target */
+ ID_GND | enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */
+ },
+ { /* Progress if we are addressed. */
+ 20, /* .test */
+ peripheral_bus_reset, /* .state */
+ peripheral_addressed, /* .target */
+ ADDRESSED, /* .test1 */
+ },
+ /*!
+ * The State Machine in the configured state for a Traditional USB Device.
+ * This means that there is an active session, there is packet traffic
+ * with this device.
+ */
+ { /* Move to idle via discharge, if we loose any of these inputs. */
+ 21, /* .test */
+ peripheral_addressed, /* .state */
+ peripheral_discharge_vbus, /* .target */
+ ID_GND | enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */
+ },
+ { /* Progress if we are configured. */
+ 22, /* .test */
+ peripheral_addressed, /* .state */
+ peripheral_configured, /* .target */
+ CONFIGURED, /* .test1 */
+ },
+ /*!
+ * The State Machine in the configured state for a Traditional USB Device.
+ * This means that there is an active session, there is packet traffic
+ * with this device.
+ */
+ { /* */
+ 23, /* .test */
+ peripheral_configured, /* .state */
+ peripheral_suspended, /* .target */
+ BUS_SUSPENDED, /* .test1 */
+ },
+ { /* Move to idle via discharge, if we loose any of these inputs. */
+ 24, /* .test */
+ peripheral_configured, /* .state */
+ peripheral_discharge_vbus, /* .target */
+ ID_GND | enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */
+ },
+ /*!
+ * The State Machine in the discharge state for a Traditional USB Device.
+ * The device has been unplugged. The Vbus discharge resistor will be enabled
+ * for the TLDISC_DSCHRG time period.
+ */
+ { /* Progress to idle on timeout. */
+ 25, /* .test */
+ peripheral_discharge_vbus, /* .state */
+ peripheral_idle, /* .target */
+ Tldisc_dschrg, /* .test1 */
+ },
+ /*!
+ * The State Machine in the suspend state for a Traditional USB Device.
+ */
+ { /* Move to idle via discharge, if we loose any of these inputs. */
+ 26, /* .test */
+ peripheral_suspended, /* .state */
+ peripheral_discharge_vbus, /* .target */
+ ID_GND | enable_otg_ | B_SESS_VLD_ | b_bus_drop, /* .test1 */
+ },
+ { /* Check for a resumed bus. */
+ 27, /* .test */
+ peripheral_suspended, /* .state */
+ peripheral_configured, /* .target */
+ BUS_SUSPENDED_, /* .test1 */
+ },
+ { /* Is remote wakeup enabled? */
+ 28, /* .test */
+ peripheral_suspended, /* .state */
+ peripheral_wakeup_enabled, /* .target */
+ REMOTE_WAKEUP_ENABLED, /* .test1 */
+ },
+ /*!
+ * The State Machine in the suspend state for a Traditional USB Device,
+ * prior to suspended the USB Host enabled Remote Wakeup by sending a
+ * set REMOTE WAKUP request.
+ */
+ { /* Move to idle via discharge, if we loose any of these inputs. */
+ 29, /* .test */
+ peripheral_wakeup_enabled, /* .state */
+ peripheral_discharge_vbus, /* .target */
+ enable_otg_ | B_SESS_VLD_, /* .test1 */
+ },
+ { /* Check for a resumed bus. */
+ 30, /* .test */
+ peripheral_wakeup_enabled, /* .state */
+ peripheral_suspended, /* .target */
+ BUS_SUSPENDED_, /* .test1 */
+ },
+ { /* Remote wakeup requested? */
+ 31, /* .test */
+ peripheral_wakeup_enabled, /* .state */
+ peripheral_wakeup, /* .target */
+ remote_wakeup_cmd, /* .test1 */
+ },
+ /*!
+ * The State Machine in the wakeup state for a Traditional USB Device,
+ * The REMOTE WAKEUP procedure will be performed.
+ */
+ { /* Automatic return. */
+ 32, /* .test */
+ peripheral_wakeup, /* .state */
+ peripheral_wakeup_enabled, /* .target */
+ AUTO | AUTO_, /* .test1 */
+ },
+ /*!
+ * A-Device idle state. An A-Plug is inserted in the Mini A-B Receptacle.
+ * This is the Host Only idle state. Waiting for user to allow
+ * the bus to be used.
+ *
+ * N.B. Reset all progress indicator inputs here.
+ */
+ { /* */
+ 33, /* .test */
+ host_idle, /* .state */
+ host_idle_dropped, /* .target */
+ a_bus_drop, /* .test1 */
+ },
+ { /* Check for ID change or disable (this must be before b_bus_drop test.) */
+ 34, /* .test */
+ host_idle, /* .state */
+ otg_enabled, /* .target */
+ ID_GND_ | enable_otg_, /* .test1 */
+ },
+ { /* is this correct? */
+ 35, /* .test */
+ host_idle, /* .state */
+ host_wait_vrise, /* .target */
+ AUTO | AUTO_, /* .test1 */
+ },
+ /*!
+ * A-Device idle state. An A-Plug is inserted in the Mini A-B Receptacle.
+ * This is the Host Only idle state. Waiting for user to allow
+ * the bus to be used.
+ *
+ * N.B. Reset all progress indicator inputs here.
+ */
+ { /* */
+ 36, /* .test */
+ host_idle_dropped, /* .state */
+ host_idle, /* .target */
+ a_bus_drop_ | ID_GND_ | enable_otg_, /* .test1 */
+ },
+ /*!
+ * First check if external charge pump is required.
+ *
+ * XXX We force a wait of Ta_wait_vrise here, arriving in host wait
+ * port connect to quickly causes system hangs occasionally.
+ */
+ { /* */
+ 37, /* .test */
+ host_wait_vrise, /* .state */
+ host_idle, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop, /* .test1 */
+ },
+ { /* */
+ 38, /* .test */
+ host_wait_vrise, /* .state */
+ host_wait_port_connect, /* .target */
+ VBUS_VLD, /* .test1 */
+ Ta_wait_vrise, /* .test2 */
+ },
+ { /* */
+ 39, /* .test */
+ host_wait_vrise, /* .state */
+ host_wait_vrise_overcurrent, /* .target */
+ Ta_wait_vrise, /* .test1 */
+ },
+ /*!
+ * A-Device Overcurrent condition (taking too long for Vbus to become valid.)
+ *
+ */
+ { /* */
+ 40, /* .test */
+ host_wait_vrise_overcurrent, /* .state */
+ host_wait_port_connect, /* .target */
+ Tst_one_second, /* .test1 */
+ },
+ /*!
+ * Wait for a connection.
+ *
+ * A-Device wait for B-Device Connect. This is the normal route using the
+ * long debounce window.
+ *
+ * This state can optionally use the Ta_wait_bcon timeout (OTG mode) or
+ * wait forever (traditional USB Host mode.)
+ *
+ * XXX Arriving in this state to quickly from wait vrise can cause a system hang.
+ */
+ { /* Vbus error? */
+ 41, /* .test */
+ host_wait_port_connect, /* .state */
+ host_vbus_err, /* .target */
+ VBUS_VLD_, /* .test1 */
+ },
+ { /* */
+ 42, /* .test */
+ host_wait_port_connect, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop, /* .test1 */
+ },
+ { /* */
+ 43, /* .test */
+ host_wait_port_connect, /* .state */
+ host_port_connected, /* .target */
+ HUB_PORT_CONNECT, /* .test1 */
+ },
+ /*!
+ * A-Device host, the host controller driver has noticed a port status change,
+ * reset the bus and proceed.
+ */
+ { /* */
+ 44, /* .test */
+ host_port_connected, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop | HUB_PORT_CONNECT_ | Tst_two_second, /* .test1 */
+ },
+ { /* */
+ 45, /* .test */
+ host_port_connected, /* .state */
+ host_bus_reset, /* .target */
+ BUS_RESET, /* .test1 */
+ },
+ { /* */
+ 46, /* .test */
+ host_port_connected, /* .state */
+ host_addressed, /* .target */
+ ADDRESSED, /* .test1 */
+ },
+ /*!
+ * A-Device host, the bus has been reset, attempt to address the device.
+ */
+ { /* */
+ 47, /* .test */
+ host_bus_reset, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop, /* .test1 */
+ },
+ { /* */
+ 48, /* .test */
+ host_bus_reset, /* .state */
+ host_addressed, /* .target */
+ ADDRESSED, /* .test1 */
+ },
+ /*!
+ * A-Device host, the device has been addressed, attempt to enumerate,
+ * find the appropriate class driver and configure.
+ */
+ { /* Lost B-Connect or user changed his mind? */
+ 49, /* .test */
+ host_addressed, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop | HUB_PORT_CONNECT_, /* .test1 */
+ },
+ { /* Device configured, class driver loaded. */
+ 50, /* .test */
+ host_addressed, /* .state */
+ host_configured, /* .target */
+ CONFIGURED, /* .test1 */
+ },
+ /*!
+ * A-Device host, the enumerated device is supported, and has been configured.
+ *
+ * XXX N.B. Currently assuming HNP enable feature is automatically performed.
+ * XXX N.B. Currently forcing HNP_CAPABLE and HNP_ENABLED.
+ */
+ { /* Vbus error? */
+ 51, /* .test */
+ host_configured, /* .state */
+ host_vbus_err, /* .target */
+ VBUS_VLD_, /* .test1 */
+ },
+ { /* */
+ 52, /* .test */
+ host_configured, /* .state */
+ host_wait_dischrg, /* .target */
+ HUB_PORT_CONNECT_, /* .test1 */
+ },
+ { /* */
+ 53, /* .test */
+ host_configured, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | enable_otg_ | a_bus_drop, /* .test1 */
+ },
+ /*!
+ * A-Device Vbus error.
+ * The user must be informed and allowed to clear the problem.
+ */
+ { /* */
+ 54, /* .test */
+ host_vbus_err, /* .state */
+ host_wait_dischrg, /* .target */
+ ID_GND_ | clr_err_cmd | a_bus_drop, /* .test1 */
+ },
+ /*!
+ */
+ { /* Normal finish, wait for a_sess_vld/ */
+ 55, /* .test */
+ host_wait_dischrg, /* .state */
+ host_wait_vfall, /* .target */
+ AUTO | AUTO_, /* .test1 */
+ },
+ /*!
+ * A-Device wait for Vbus to fall.
+ *
+ * XXX Currently reseting a_bus_req on entry, require explicit a_bus_req to proceed.
+ */
+ { /* Normal finish, wait for a_sess_vld/ */
+ 56, /* .test */
+ host_wait_vfall, /* .state */
+ host_idle, /* .target */
+ A_SESS_VLD_ | Tst_one_second, /* .test1 */
+ },
+ /*!
+ * This is not an OTG State. It is used internally to mark the end of the
+ * list of states and inputs.
+ */
+ { /* */
+ 57, /* .test */
+ terminator_state, /* .state */
+ invalid_state, /* .target */
+ 0, /* .test1 */
+ },
+ {58, invalid_state,},
+
+};
+
+#define OTG_TESTS_MN 58
+
+int otg_test_max_mn = 58;
+
+ /* eof */
+
+/* Generated by otg-info-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+struct otg_state otg_states_mn[OTG_STATES_MN + 1] = {
+ { /* 0 */
+ invalid_state, /* .state */
+ m_otg_init, /* .meta */
+ "invalid_state", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 1 */
+ otg_disabled, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disabled", /* .name */
+ 0, /* .tmout */
+ enable_otg_ | PCD_OK_ | TCD_OK_ | HCD_OK_, /* .reset */
+ /* .outputs */
+ chrg_vbus_out_ | drv_vbus_out_ | charge_pump_out_ | dm_pullup_out_ | dp_pullup_out_ | loc_sof_out_ |
+ hcd_rh_out_,
+ },
+ { /* 2 */
+ otg_disable_tcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disable_tcd", /* .name */
+ 0, /* .tmout */
+ TCD_OK_, /* .reset */
+ /* .outputs */
+ tcd_init_out_,
+ },
+ { /* 3 */
+ otg_disable_hcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disable_hcd", /* .name */
+ 0, /* .tmout */
+ HCD_OK_, /* .reset */
+ /* .outputs */
+ hcd_init_out_,
+ },
+ { /* 4 */
+ otg_disable_pcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disable_pcd", /* .name */
+ 0, /* .tmout */
+ PCD_OK_, /* .reset */
+ /* .outputs */
+ pcd_init_out_,
+ },
+ { /* 5 */
+ otg_disable_ocd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_disable_ocd", /* .name */
+ 0, /* .tmout */
+ OCD_OK_, /* .reset */
+ /* .outputs */
+ ocd_init_out_,
+ },
+ { /* 6 */
+ otg_enable_ocd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_enable_ocd", /* .name */
+ 0, /* .tmout */
+ OCD_OK_, /* .reset */
+ /* .outputs */
+ ocd_init_out,
+ },
+ { /* 7 */
+ otg_enable_pcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_enable_pcd", /* .name */
+ 0, /* .tmout */
+ PCD_OK_, /* .reset */
+ /* .outputs */
+ pcd_init_out,
+ },
+ { /* 8 */
+ otg_enable_hcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_enable_hcd", /* .name */
+ 0, /* .tmout */
+ HCD_OK_, /* .reset */
+ /* .outputs */
+ hcd_init_out,
+ },
+ { /* 9 */
+ otg_enable_tcd, /* .state */
+ m_otg_init, /* .meta */
+ "otg_enable_tcd", /* .name */
+ 0, /* .tmout */
+ TCD_OK_, /* .reset */
+ /* .outputs */
+ tcd_init_out,
+ },
+ { /* 10 */
+ otg_enabled, /* .state */
+ m_otg_init, /* .meta */
+ "otg_enabled", /* .name */
+ TST_ONE_SECOND, /* .tmout */
+ b_bus_drop_, /* .reset */
+ /* .outputs */
+ dm_det_out_ | dp_det_out_,
+ },
+ { /* 11 */
+ peripheral_idle, /* .state */
+ m_b_idle, /* .meta */
+ "peripheral_idle", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ dp_pullup_out_ | hcd_rh_out | pcd_en_out_ | tcd_en_out_power | dischrg_vbus_out_ | dp_pulldown_out |
+ dm_pulldown_out | hcd_en_out_,
+ },
+ { /* 12 */
+ peripheral_dropped, /* .state */
+ m_b_idle, /* .meta */
+ "peripheral_dropped", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 13 */
+ peripheral_wait, /* .state */
+ m_b_peripheral, /* .meta */
+ "peripheral_wait", /* .name */
+ 0, /* .tmout */
+ BUS_RESET_, /* .reset */
+ /* .outputs */
+ dp_pullup_out | pcd_en_out | dp_pulldown_out_ | dm_pulldown_out_ | hcd_en_out_,
+ },
+ { /* 14 */
+ peripheral_bus_reset, /* .state */
+ m_b_peripheral, /* .meta */
+ "peripheral_bus_reset", /* .name */
+ 0, /* .tmout */
+ BUS_RESET_ | ADDRESSED_, /* .reset */
+ },
+ { /* 15 */
+ peripheral_addressed, /* .state */
+ m_b_peripheral, /* .meta */
+ "peripheral_addressed", /* .name */
+ 0, /* .tmout */
+ ADDRESSED_ | CONFIGURED_ | REMOTE_WAKEUP_ENABLED_, /* .reset */
+ },
+ { /* 16 */
+ peripheral_configured, /* .state */
+ m_b_peripheral, /* .meta */
+ "peripheral_configured", /* .name */
+ 0, /* .tmout */
+ CONFIGURED_ | BUS_SUSPENDED_, /* .reset */
+ },
+ { /* 17 */
+ peripheral_discharge_vbus, /* .state */
+ m_b_peripheral, /* .meta */
+ "peripheral_discharge_vbus", /* .name */
+ TLDISC_DSCHRG, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ dp_pullup_out_ | dischrg_vbus_out | pcd_en_out_ | hcd_en_out_,
+ },
+ { /* 18 */
+ peripheral_suspended, /* .state */
+ m_b_suspended, /* .meta */
+ "peripheral_suspended", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 19 */
+ peripheral_wakeup_enabled, /* .state */
+ m_b_suspended, /* .meta */
+ "peripheral_wakeup_enabled", /* .name */
+ 0, /* .tmout */
+ remote_wakeup_cmd_, /* .reset */
+ },
+ { /* 20 */
+ peripheral_wakeup, /* .state */
+ m_b_suspended, /* .meta */
+ "peripheral_wakeup", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ remote_wakeup_out_power,
+ },
+ { /* 21 */
+ host_idle, /* .state */
+ m_a_idle, /* .meta */
+ "host_idle", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ chrg_vbus_out_ | hcd_en_out | hcd_rh_out_ | dp_pulldown_out | dm_pulldown_out | HUB_PORT_CONNECT_ |
+ pcd_en_out_,
+ },
+ { /* 22 */
+ host_idle_dropped, /* .state */
+ m_a_idle, /* .meta */
+ "host_idle_dropped", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 23 */
+ host_wait_vrise, /* .state */
+ m_a_wait_vrise, /* .meta */
+ "host_wait_vrise", /* .name */
+ TA_WAIT_VRISE, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ drv_vbus_out,
+ },
+ { /* 24 */
+ host_wait_vrise_overcurrent, /* .state */
+ m_a_wait_vrise, /* .meta */
+ "host_wait_vrise_overcurrent", /* .name */
+ TST_ONE_SECOND, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 25 */
+ host_wait_port_connect, /* .state */
+ m_a_wait_bcon, /* .meta */
+ "host_wait_port_connect", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ loc_sof_out_ | loc_suspend_out_ | dp_pullup_out_ | hcd_rh_out,
+ },
+ { /* 26 */
+ host_port_connected, /* .state */
+ m_a_host, /* .meta */
+ "host_port_connected", /* .name */
+ TST_TWO_SECOND * 5, /* .tmout */
+ BUS_RESET_, /* .reset */
+ /* .outputs */
+ loc_sof_out,
+ },
+ { /* 27 */
+ host_bus_reset, /* .state */
+ m_a_host, /* .meta */
+ "host_bus_reset", /* .name */
+ 0, /* .tmout */
+ a_suspend_req_, /* .reset */
+ },
+ { /* 28 */
+ host_addressed, /* .state */
+ m_a_host, /* .meta */
+ "host_addressed", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ },
+ { /* 29 */
+ host_configured, /* .state */
+ m_a_host, /* .meta */
+ "host_configured", /* .name */
+ TST_TWO_SECOND, /* .tmout */
+ CONFIGURED_ | HNP_CAPABLE | HNP_ENABLED, /* .reset */
+ },
+ { /* 30 */
+ host_vbus_err, /* .state */
+ m_a_vbus_err, /* .meta */
+ "host_vbus_err", /* .name */
+ 0, /* .tmout */
+ clr_err_cmd_ | a_suspend_req_, /* .reset */
+ /* .outputs */
+ hcd_en_out | pcd_en_out_ | drv_vbus_out_ | charge_pump_out_ | dp_pullup_out_ | loc_sof_out_ | loc_suspend_out_
+ | dp_pulldown_out | dm_pulldown_out | hcd_rh_out_,
+ },
+ { /* 31 */
+ host_wait_dischrg, /* .state */
+ m_a_wait_vfall, /* .meta */
+ "host_wait_dischrg", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ drv_vbus_out_ | charge_pump_out_ | loc_sof_out_ | tcd_en_out_power | hcd_rh_out_ | dischrg_vbus_out,
+ },
+ { /* 32 */
+ host_wait_vfall, /* .state */
+ m_a_wait_vfall, /* .meta */
+ "host_wait_vfall", /* .name */
+ TST_ONE_SECOND, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ hcd_en_out_ | loc_suspend_out_ | pcd_en_out_ | chrg_vbus_out_,
+ },
+ { /* 33 */
+ terminator_state, /* .state */
+ m_otg_init, /* .meta */
+ "terminator_state", /* .name */
+ 0, /* .tmout */
+ 0, /* .reset */
+ /* .outputs */
+ 0,
+ },
+
+ {0, 0, "", 0, 0,},
+
+};
+
+struct otg_firmware otg_firmware_mn = {
+ OTG_STATES_MN, /* number of states */
+ OTG_TESTS_MN, /* number of tests */
+ "otg-mn", /* name of firmware */
+ otg_states_mn, /* struct otg_state * */
+ otg_tests_mn, /* struct otg_test * */
+};
+
+/* eof */
diff --git a/drivers/otg/otgcore/otg-fw.c b/drivers/otg/otgcore/otg-fw.c
new file mode 100644
index 000000000000..cc4ded30c478
--- /dev/null
+++ b/drivers/otg/otgcore/otg-fw.c
@@ -0,0 +1,274 @@
+/*
+ * 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
+ */
+
+/* Generated by otg-fw-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+/*!
+* @file otg/otgcore/otg-fw.c
+* @brief OTG Firmware - Input, Output and Timeout definitions
+*
+* This file defines the OTG State Machine input, output and timeout constants.
+*
+*
+* @ingroup OTGFW
+*/
+
+/*!
+* @page OTGFW
+* @section OTGFW_SECTION - otg-fw.c
+* This contains the input, output and timout definitions for the OTG state machine firmware
+*/
+
+
+#ifdef OTG_APPLICATION
+#include "otg-fw.h"
+#else /* OTG_APPLICATION/ */
+#include <otg/otg-compat.h>
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#endif /* OTG_APPLICATION */
+
+#define OTG_VERSION_FWC 200704251606L
+
+
+#if OTG_VERSION_FWC != OTG_VERSION_FW
+#error OTG_VERSION_FWC != OTG_VERSION_FW
+#endif /* OTG_VERSION_FWC != OTG_VERSION_FW */
+
+/* Generated by otg-input-name-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+
+/* #include "otg-fw.h" */
+
+#define OTG_VERSION_INPUT_TABLE 200704251606L
+
+
+#if OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW
+#error OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW
+#endif /* OTG_VERSION_INPUT_TABLE != OTG_VERSION_FW */
+
+
+struct otg_input_name otg_input_names[] = {
+ {ID_GND, "ID_GND",},
+ {ID_FLOAT, "ID_FLOAT",},
+ {DP_HIGH, "DP_HIGH",},
+ {DM_HIGH, "DM_HIGH",},
+ {B_SESS_END, "B_SESS_END",},
+ {A_SESS_VLD, "A_SESS_VLD",},
+ {B_SESS_VLD, "B_SESS_VLD",},
+ {VBUS_VLD, "VBUS_VLD",},
+ {SRP_DET, "SRP_DET",},
+ {SE0_DET, "SE0_DET",},
+ {SE1_DET, "SE1_DET",},
+ {BDIS_ACON, "BDIS_ACON",},
+ {CR_INT_DET, "CR_INT_DET",},
+ {HUB_PORT_CONNECT, "HUB_PORT_CONNECT",},
+ {BUS_RESET, "BUS_RESET",},
+ {ADDRESSED, "ADDRESSED",},
+ {DEVICE_REQUEST, "DEVICE_REQUEST",},
+ {CONFIGURED, "CONFIGURED",},
+ {NOT_SUPPORTED, "NOT_SUPPORTED",},
+ {BUS_SUSPENDED, "BUS_SUSPENDED",},
+ {a_bcon_no_tmout_req, "a_bcon_no_tmout_req",},
+ {a_hpwr_req, "a_hpwr_req",},
+ {bus_drop, "bus_drop",},
+ {a_bus_drop, "a_bus_drop",},
+ {b_bus_drop, "b_bus_drop",},
+ {bus_req, "bus_req",},
+ {a_bus_req, "a_bus_req",},
+ {b_bus_req, "b_bus_req",},
+ {b_sess_req, "b_sess_req",},
+ {suspend_req, "suspend_req",},
+ {a_suspend_req, "a_suspend_req",},
+ {b_suspend_req, "b_suspend_req",},
+ {set_remote_wakeup_cmd, "set_remote_wakeup_cmd",},
+ {remote_wakeup_cmd, "remote_wakeup_cmd",},
+ {reset_remote_wakeup_cmd, "reset_remote_wakeup_cmd",},
+ {clr_err_cmd, "clr_err_cmd",},
+ {b_hnp_cmd, "b_hnp_cmd",},
+ {ph_int_cmd, "ph_int_cmd",},
+ {ph_audio_cmd, "ph_audio_cmd",},
+ {cr_int_cmd, "cr_int_cmd",},
+ {led_on_cmd, "led_on_cmd",},
+ {led_off_cmd, "led_off_cmd",},
+ {HNP_ENABLED, "HNP_ENABLED",},
+ {HNP_CAPABLE, "HNP_CAPABLE",},
+ {HNP_SUPPORTED, "HNP_SUPPORTED",},
+ {REMOTE_WAKEUP_ENABLED, "REMOTE_WAKEUP_ENABLED",},
+ {REMOTE_CAPABLE, "REMOTE_CAPABLE",},
+ {PCD_OK, "PCD_OK",},
+ {TCD_OK, "TCD_OK",},
+ {HCD_OK, "HCD_OK",},
+ {OCD_OK, "OCD_OK",},
+ {TMOUT, "TMOUT",},
+ {enable_otg, "enable_otg",},
+ {AUTO, "AUTO",},
+ {0, "0",},
+
+};
+
+ /* eof */
+
+/* Generated by otg-ioctls-names-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+#if defined(OTG_LINUX) || defined(OTG_WINCE)
+
+
+/* #include "otg-fw.h" */
+
+#define OTG_VERSION_IOCTL_TABLE 200704251606L
+
+
+#if OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW
+#error OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW
+#endif /* OTG_VERSION_IOCTL_TABLE != OTG_VERSION_FW */
+
+struct otg_ioctl_name otg_ioctl_names[] = {
+ {OTGADMIN_A_BCON_NO_TMOUT_REQ, a_bcon_no_tmout_req, "OTGADMIN_A_BCON_NO_TMOUT_REQ",},
+ {OTGADMIN_A_HPWR_REQ, a_hpwr_req, "OTGADMIN_A_HPWR_REQ",},
+ {OTGADMIN_BUS_DROP, bus_drop, "OTGADMIN_BUS_DROP",},
+ {OTGADMIN_A_BUS_DROP, a_bus_drop, "OTGADMIN_A_BUS_DROP",},
+ {OTGADMIN_B_BUS_DROP, b_bus_drop, "OTGADMIN_B_BUS_DROP",},
+ {OTGADMIN_BUS_REQ, bus_req, "OTGADMIN_BUS_REQ",},
+ {OTGADMIN_A_BUS_REQ, a_bus_req, "OTGADMIN_A_BUS_REQ",},
+ {OTGADMIN_B_BUS_REQ, b_bus_req, "OTGADMIN_B_BUS_REQ",},
+ {OTGADMIN_B_SESS_REQ, b_sess_req, "OTGADMIN_B_SESS_REQ",},
+ {OTGADMIN_SUSPEND_REQ, suspend_req, "OTGADMIN_SUSPEND_REQ",},
+ {OTGADMIN_A_SUSPEND_REQ, a_suspend_req, "OTGADMIN_A_SUSPEND_REQ",},
+ {OTGADMIN_B_SUSPEND_REQ, b_suspend_req, "OTGADMIN_B_SUSPEND_REQ",},
+ {OTGADMIN_SET_REMOTE_WAKEUP_CMD, set_remote_wakeup_cmd, "OTGADMIN_SET_REMOTE_WAKEUP_CMD",},
+ {OTGADMIN_REMOTE_WAKEUP_CMD, remote_wakeup_cmd, "OTGADMIN_REMOTE_WAKEUP_CMD",},
+ {OTGADMIN_RESET_REMOTE_WAKEUP_CMD, reset_remote_wakeup_cmd, "OTGADMIN_RESET_REMOTE_WAKEUP_CMD",},
+ {OTGADMIN_CLR_ERR_CMD, clr_err_cmd, "OTGADMIN_CLR_ERR_CMD",},
+ {OTGADMIN_B_HNP_CMD, b_hnp_cmd, "OTGADMIN_B_HNP_CMD",},
+ {OTGADMIN_PH_INT_CMD, ph_int_cmd, "OTGADMIN_PH_INT_CMD",},
+ {OTGADMIN_PH_AUDIO_CMD, ph_audio_cmd, "OTGADMIN_PH_AUDIO_CMD",},
+ {OTGADMIN_CR_INT_CMD, cr_int_cmd, "OTGADMIN_CR_INT_CMD",},
+ {OTGADMIN_LED_ON_CMD, led_on_cmd, "OTGADMIN_LED_ON_CMD",},
+ {OTGADMIN_LED_OFF_CMD, led_off_cmd, "OTGADMIN_LED_OFF_CMD",},
+ {OTGADMIN_ENABLE_OTG, enable_otg, "OTGADMIN_ENABLE_OTG",},
+ {0, 0, "",},
+
+};
+
+ /* eof */
+#endif /* defined(OTG_LINUX) || defined(OTG_WINCE) */
+
+/* Generated by otg-output-names-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+char *otg_output_names[] = {
+ "tcd_init_out", /* 0 Initiate Transceiver Controller Driver Initialization (or De-initialization.) */
+ "pcd_init_out", /* 1 Initiate Peripheral Controller Driver Initialization (or De-initialization.) */
+ "hcd_init_out", /* 2 Initiate Host Controller Driver Initialization (or De-initialization). */
+ "ocd_init_out", /* 3 Initiate OTG Controller Driver Initialization (or De-initialization). */
+ "tcd_en_out", /* 4 Enable Transceiver Controller Driver */
+ "pcd_en_out", /* 5 Enable Peripheral Controller Driver */
+ "hcd_en_out", /* 6 Enable Host Controller Driver */
+ "drv_vbus_out", /* 7 A-Device will Drive Vbus to 5V through charge pump. */
+ "chrg_vbus_out", /* 8 B-Device will charge Vbus to 3.3V through resistor (SRP.) */
+ "dischrg_vbus_out", /* 9 B-Device will discharge Vbus (enable dischage resistor.) */
+ "dm_pullup_out", /* 10 DM pullup control - aka loc_carkit */
+ "dm_pulldown_out", /* 11 DM pulldown control */
+ "dp_pullup_out", /* 12 DP pullup control - aka loc_conn */
+ "dp_pulldown_out", /* 13 DP pulldown control */
+ "clr_overcurrent_out", /* 14 Clear overcurrent indication */
+ "dm_det_out", /* 15 Enable B-Device D- High detect */
+ "dp_det_out", /* 16 Enable B-Device D+ High detect */
+ "cr_det_out", /* 17 Enable D+ CR detect */
+ "charge_pump_out", /* 18 Enable external charge pump. */
+ "bdis_acon_out", /* 19 Enable auto A-connect after B-disconnect. */
+ "id_pulldown_out", /* 20 Enable the ID to ground pulldown ( (CEA-936 - 5 wire carkit.) */
+ "uart_out", /* 21 Enable Transparent UART mode (CEA-936.) */
+ "audio_out", /* 22 Enable Audio mode (CEA-936 CarKit interrupt detector.) */
+ "mono_out", /* 23 Enable Mono-Audio mode (CEA-936.) */
+ "remote_wakeup_out", /* 24 Peripheral will perform remote wakeup. */
+ "hcd_rh_out", /* 25 Host will enable root hub */
+ "loc_sof_out", /* 26 Host will enable packet traffic. */
+ "loc_suspend_out", /* 27 Host will suspend bus. */
+ "remote_wakeup_en_out", /* 28 Host will send remote wakeup enable or disable request. */
+ "hnp_en_out", /* 29 Host will send HNP enable request. */
+ "hpwr_out", /* 30 Host will enable high power (external charge pump.) */
+ NULL,
+
+};
+
+ /* eof */
+
+/* Generated by otg-meta-names-c.awk
+ *
+ * Do not Edit, see otg-state.awk
+ */
+
+/* %Z %K */
+
+
+
+char *otg_meta_names[] = {
+ "a_idle", /* 0 - */
+ "a_wait_vrise", /* 1 - */
+ "a_wait_bcon", /* 2 - */
+ "a_host", /* 3 - */
+ "a_suspend", /* 4 - */
+ "a_peripheral", /* 5 - */
+ "a_wait_vfall", /* 6 - */
+ "a_vbus_err", /* 7 - */
+ "b_idle", /* 8 - */
+ "b_srp_init", /* 9 - */
+ "b_peripheral", /* 10 - */
+ "b_suspend", /* 11 - */
+ "b_wait_acon", /* 12 - */
+ "b_host", /* 13 - */
+ "b_suspended", /* 14 - */
+ "ph_disc", /* 15 - Equivalent to b_peripheral */
+ "ph_init", /* 16 - */
+ "ph_uart", /* 17 - */
+ "ph_aud", /* 18 - */
+ "ph_wait", /* 19 - */
+ "ph_exit", /* 20 - */
+ "cr_init", /* 21 - */
+ "cr_uart", /* 22 - */
+ "cr_aud", /* 23 - */
+ "cr_ack", /* 24 - */
+ "cr_wait", /* 25 - */
+ "cr_disc", /* 26 - */
+ "otg_init", /* 27 - */
+ "usb_accessory", /* 28 - */
+ "usb_factory", /* 29 - */
+ "unknown", /* 30 - */
+
+ "",
+};
+
+/* eof */
diff --git a/drivers/otg/otgcore/otg-linux.c b/drivers/otg/otgcore/otg-linux.c
new file mode 100644
index 000000000000..2f55601b12fe
--- /dev/null
+++ b/drivers/otg/otgcore/otg-linux.c
@@ -0,0 +1,900 @@
+/*
+ * 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
+ */
+#if 0
+/*
+ * otg/otgcore/otg-linux.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/otg-linux.c|20070920072547|06330
+ *
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ */
+xxx
+xxx
+
+/*!
+ * @file otg/otgcore/otg-linux.c
+ * @brief Linux OS Compatibility defines
+ *
+ * @ingroup OTGINIT
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX)
+
+#if !defined(_OTG_MODULE_H)
+#include <otg/otg-module.h>
+#endif
+
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/workqueue.h>
+
+#if !defined(LINUX26) && !defined(LINUX24)
+#error "One of LINUX24 and LINUX26 needs to be defined here"
+#endif
+
+#if defined(LINUX26)
+#include <linux/device.h>
+#endif
+
+
+/* ********************************************************************************************** */
+
+/*! @name Compilation Related
+ */
+
+/*@{*/
+#undef PRAGMAPACK
+/** Macros to support packed structures **/
+#define PACKED1 __attribute__((packed))
+#define PACKED2
+#define PACKED0
+
+/** Macros to support packed enum's **/
+#define PACKED_ENUM enum
+#define PACKED_ENUM_EXTRA
+
+#define INLINE __inline__
+
+/*@}*/
+
+/* ********************************************************************************************** */
+/* otg-trace - included so that TRACE_MSG can be used if desired to debug
+ * the otg-xxx implementation
+ */
+
+#include <otg/otg-trace.h>
+
+
+/* ********************************************************************************************** */
+
+
+/* ********************************************************************************************** */
+/*!
+ * @name Task Allocation
+ *
+ * Long lived, low priority, high latency tasks.
+ *
+ * Tasks may use semaphores, mutexes, atomic operations and memory allocations.
+ * @{
+ *
+ * TASKS are long lived, MAY HAVE low priority and high latency, the typical
+ * implementation has the work item wait in a semaphore waiting for work. :w
+ *
+ * The TASK work functions are structured to run until terminated, using
+ * a semaphore to wait for work.
+ *
+ * TASK creation:
+ * struct otg_task *task = otg_task_init("taskname", task_work, TAG);
+ * if (task)
+ * otg_task_start(task);
+ *
+ * TASK termination:
+ * otg_task_exit(task);
+ *
+ * TASK work notification
+ * otg_up_work(task);
+ *
+ * TASK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_task *task = (struct otg_task *)data;
+ * void *mydata = task->data;
+ * otg_up_admin(task);
+ * do {
+ * // wait for work
+ * otg_down_work(task);
+ * // do work
+ * } while (!task->terminating);
+ * task->terminated = TRUE;
+ * otg_up_admin(task);
+ * }
+ *
+ * static struct otg_task *otg_task_init(char *name, void (*work) (void *), void *data, otg_tag_t tag)
+ * static void otg_up_work(struct otg_task *task)
+ * static void otg_up_admin(struct otg_task *task)
+ * static void otg_down_work(struct otg_task *task)
+ * static void otg_down_admin(struct otg_task *task)
+ * static void otg_task_start(struct otg_task *task)
+ * static void otg_task_exit(struct otg_task *task)
+ *
+ */
+
+/* XXX OTG_TASK_WORK
+ *
+ * Linux Implementation Configuration
+ *
+ * #define OTG_TASK_WORK
+ * Use work items only for TASK implemenation
+ */
+
+#define OTG_TASK_WORK
+//#undef OTG_TASK_WORK
+
+#if defined(LINUX26)
+ #define SCHEDULE_WORK(item) schedule_work(&item)
+#else /* LINUX26 */
+ #define SCHEDULE_WORK(item) schedule_task(&item)
+#endif /* LINUX26 */
+
+/*! struct otg_task
+ */
+typedef void *otg_task_arg_t;
+typedef void (* otg_task_proc_t) (otg_task_arg_t data);
+struct otg_task {
+ #if !defined(OTG_TASK_WORK)
+ struct workqueue_struct *work_queue;
+ otg_sem_t admin_sem;
+ otg_sem_t work_sem;
+ #endif /* defined(OTG_TASK_WORK) */
+ struct work_struct work;
+ otg_task_proc_t proc;
+ BOOL terminate;
+ BOOL terminated;
+ otg_task_arg_t *data;
+ BOOL debug;
+ otg_tag_t tag;
+ char *name;
+};
+
+static void inline otg_sleep(int n)
+{
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout( n * HZ );
+}
+
+/*! otg_up_admin
+ * @brief Signal start task to re-start.
+ * @param task - otg_task instance pointer
+ */
+void otg_up_admin(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "UP ADMIN: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ up(&task->admin_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_down_work
+ *@brief Used by work task to wait.
+ *@param task - otg_task instance pointer
+ */
+void otg_down_work(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN WORK: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ while (down_interruptible(&task->work_sem));
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_task_proc - otg task handler
+ * @param data - otg_task instance pointer
+ */
+void otg_task_proc(otg_task_arg_t data)
+{
+ struct otg_task *task = (struct otg_task *)data;
+ otg_up_admin(task);
+ do {
+ // wait for work
+ if (task->debug)
+ printk(KERN_INFO"%s: DOWN %s\n", __FUNCTION__, task->name);
+
+ otg_down_work(task);
+
+ if (task->debug)
+ printk(KERN_INFO"%s: WORKING %s\n", __FUNCTION__, task->name);
+
+ task->proc(task->data);
+
+ } while (!task->terminate);
+ if (task->debug)
+ printk(KERN_INFO"%s: TERMINATING %s\n", __FUNCTION__, task->name);
+ task->terminated = TRUE;
+ otg_up_admin(task);
+}
+
+/*! otg_task_init
+ *@brief Create otg task structure, create workqueue, initialize it.
+ *@param name - name of task or workqueue
+ *@param proc - handler
+ *@param data - parameter pointer for handler
+ *@param tag-
+ *@return initialized otg_task instance pointer
+ */
+struct otg_task *otg_task_init2(char *name, otg_task_proc_t proc, otg_task_arg_t data, otg_tag_t tag)
+{
+ struct otg_task *task;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+
+ RETURN_NULL_UNLESS((task = CKMALLOC(sizeof (struct otg_task))));
+
+ task->tag = tag;
+ task->data = data;
+ task->name = name;
+ task->proc = proc;
+
+ #if defined(OTG_TASK_WORK)
+ task->terminated = task->terminate = TRUE;
+ #else /* defined(OTG_TASK_WORK) */
+ task->terminated = task->terminate = FALSE;
+ #if defined(LINUX26)
+ THROW_UNLESS((task->work_queue = create_singlethread_workqueue(name)), error);
+ #else /* LINUX26 */
+ THROW_UNLESS((task->work_queue = create_workqueue(name)), error);
+ #endif /* LINUX26 */
+ init_MUTEX_LOCKED(&task->admin_sem);
+ init_MUTEX_LOCKED(&task->work_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+
+ INIT_WORK(&task->work, otg_task_proc, task);
+
+ return task;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (task) LKFREE(task);
+ return NULL;
+ }
+}
+
+/*! otg_up_work
+ * Signal work param taskto re-start.
+ * @param task - otg_task instance pointer
+ */
+void otg_up_work(struct otg_task *task)
+{
+ //TRACE_STRING(task->tag, "UP WORK: %s", task->name);
+
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(OTG_TASK_WORK)
+ task->terminated = FALSE;
+ SCHEDULE_WORK(task->work);
+ #else /* defined(OTG_TASK_WORK) */
+ up(&task->work_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_down_admin
+ * Used by admin task to wait.
+ * @param task - otg_task instance pointer
+ */
+void otg_down_admin(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "DOWN ADMIN: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+ while (down_interruptible(&task->admin_sem));
+ //DOWN(&task->admin_sem);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_task_start
+ * Start and wait for otg task.
+ * @param task - otg_task instance pointer
+ */
+void otg_task_start(struct otg_task *task)
+{
+ #if defined(OTG_TASK_WORK)
+ /* NOTHING */
+ #else /* defined(OTG_TASK_WORK) */
+ //TRACE_STRING(task->tag, "START: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ /* schedule work item and wait until it starts */
+ queue_work(task->work_queue, &task->work);
+ otg_down_admin(task);
+ #endif /* defined(OTG_TASK_WORK) */
+}
+
+/*! otg_task_exit
+ * Terminate and wait for otg task.
+ * @param task - otg_task instance pointer
+ */
+void otg_task_exit(struct otg_task *task)
+{
+ TRACE_STRING(task->tag, "EXIT: %s", task->name);
+ if (task->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, task->name);
+
+ #if defined(OTG_TASK_WORK)
+ while (!task->terminated) {
+ otg_sleep( 1 );
+ }
+ #else /* defined(OTG_TASK_WORK) */
+ /* signal termination */
+ task->terminate = TRUE;
+ otg_up_work(task);
+ otg_down_admin(task);
+
+ /* destroy workqueue */
+ flush_workqueue(task->work_queue);
+ destroy_workqueue(task->work_queue);
+ #endif /* defined(OTG_TASK_WORK) */
+
+ LKFREE(task);
+}
+
+/*! otg_sleep -schedule current task to start before n seconds
+ * @param n - timeout for schedule
+ */
+
+
+/* ********************************************************************************************** */
+/*!
+ * @name WorkItem Allocation
+ *
+ * Short lived, low priority, high latency task.
+ *
+ * @{
+ *
+ * Work items are similar to tasks except that they are not expected
+ * to run for long periods of time or wait.
+ *
+ * A Work item function is run once and exits when the work
+ * is finished.
+ *
+ * static struct otg_workitem *otg_workitem_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_workitem_start(struct otg_workitem *tasklet)
+ * static void otg_workitem_exit(struct otg_workitem *tasklet)
+ *
+ * WORK creation:
+ * struct otg_workitem *tasklet = otg_workitem_init("taskletname", tasklet_work, TAG);
+ *
+ * WORK start:
+ * otg_workitem_start(tasklet);
+ *
+ * WORK termination:
+ * otg_workitem_exit(tasklet);
+ *
+ * WORK work process
+ *
+ * void task_work(void *data)
+ * {
+ * struct otg_workitem *tasklet = (struct otg_workitem *)data;
+ * void *mydata = task->data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+
+/*! struct otg_workitem
+ */
+typedef void *otg_workitem_arg_t;
+typedef void (* otg_workitem_proc_t) (otg_task_arg_t data);
+
+struct otg_workitem {
+ struct work_struct work;
+ otg_workitem_proc_t proc;
+ BOOL terminate;
+ BOOL terminated;
+ otg_task_arg_t *data;
+ BOOL debug;
+ otg_tag_t tag;
+ char *name;
+};
+
+/*! otg_workitem_run - run workitem process
+ * @param data - workite poiner
+ */
+static inline void otg_workitem_run(void *data)
+{
+ struct otg_workitem *workitem = (struct otg_workitem *)data;
+
+ if (workitem->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, workitem->name);
+
+ workitem->proc(workitem->data); // XXX convert to workitem->data
+ workitem->terminated = TRUE;
+
+ if (workitem->debug)
+ printk(KERN_INFO"%s: %s finished\n", __FUNCTION__, workitem->name);
+
+}
+
+/*! otg_workitem_init
+ * Create otg work structure, create workqueue, initialize it.
+ * @param name - workitem name
+ * @param proc - workitem handler
+ * @param data - workitem data
+ * @param tag - otg tag
+ * @return otg_workitem instance pointer
+ */
+static inline struct otg_workitem *otg_workitem_init(char *name, otg_workitem_proc_t proc, otg_workitem_arg_t data, otg_tag_t tag)
+{
+ struct otg_workitem *workitem;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+
+ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((workitem = CKMALLOC(sizeof (struct otg_workitem))));
+
+ workitem->data = data;
+ workitem->tag = tag;
+ workitem->name = name;
+ workitem->proc = proc;
+ //workitem->debug = TRUE;
+
+ workitem->terminated = workitem->terminate = TRUE;
+
+ INIT_WORK(&workitem->work, otg_workitem_run, workitem);
+
+ return workitem;
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ if (workitem) LKFREE(workitem);
+ return NULL;
+ }
+}
+
+/*! otg_workitem_start
+ * Signal work work to run.
+ * @param workitem - workitem pointer
+ */
+static void inline otg_workitem_start(struct otg_workitem *workitem)
+{
+ //TRACE_STRING(workitem->tag, "START: %s", workitem->name);
+
+ if (workitem->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, workitem->name);
+
+ workitem->proc(workitem->data);
+ workitem->terminated = FALSE;
+ SCHEDULE_WORK(workitem->work);
+}
+
+/*! otg_workitem_exit
+ * Terminate and wait for otg work.
+ * @param workitem - workitem pointer
+ */
+static void inline otg_workitem_exit(struct otg_workitem *workitem)
+{
+ //TRACE_STRING(workitem->tag, "EXIT: %s", workitem->name);
+ if (workitem->debug)
+ printk(KERN_INFO"%s: %s terminating\n", __FUNCTION__, workitem->name);
+
+ while (!workitem->terminated) {
+ otg_sleep( 1 );
+ }
+
+ if (workitem->debug)
+ printk(KERN_INFO"%s: %s terminated\n", __FUNCTION__, workitem->name);
+
+ LKFREE(workitem);
+}
+
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name Tasklet Allocation
+ *
+ * Short lived, high priority, low latency tasklets.
+ *
+ * Tasklets MAY NOT use memory allocation or wait on semaphores.
+ *
+ * Taskslets may use signal semaphores, mutexes and atomic operations.
+ * @{
+ *
+ * TASKLETS are short lived, MUST run with low latency and SHOULD have high
+ * priority.
+ *
+ * The TASKLET work function is structure to run once and exit when the work
+ * is finished.
+ *
+ * static struct otg_tasklet *otg_tasklet_init(char *name, void (*work) (unsigned long), unsigned long data, otg_tag_t tag)
+ * static void otg_tasklet_start(struct otg_tasklet *tasklet)
+ * static void otg_tasklet_exit(struct otg_tasklet *tasklet)
+ *
+ * TASKLET creation:
+ * struct otg_tasklet *tasklet = otg_tasklet_init("taskletname", tasklet_work, TAG);
+ *
+ * TASKLET start:
+ * otg_tasklet_start(tasklet);
+ *
+ * TASKLET termination:
+ * otg_tasklet_exit(tasklet);
+ *
+ * TASKLET work process
+ *
+ * void task_work(otg_tasklet_arg_t data)
+ * {
+ * void *mydata = data;
+ *
+ * // do work
+ *
+ * }
+ *
+ */
+/* XXX OTG_TASKLET_WORK
+ *
+ * Linux Implementation Configuration
+ *
+ * #define OTG_TASKLET_WORK
+ * Use work items only for TASK implemenation
+ *
+ * N.B. the Linux work item implementation for tasklets is not suitable
+ * for full OTG Dual-Role implemenation.
+ *
+ */
+
+#define OTG_TASKLET_WORK
+#undef OTG_TASKLET_WORK
+
+/*! struct otg_tasklet
+ */
+#ifdef OTG_TASKLET_WORK
+typedef void *otg_tasklet_arg_t;
+#else /* OTG_TASKLET_WORK */
+typedef unsigned long otg_tasklet_arg_t;
+#endif /* OTG_TASKLET_WORK */
+typedef void (* otg_tasklet_proc_t) (otg_tasklet_arg_t data);
+struct otg_tasklet {
+ #ifdef OTG_TASKLET_WORK
+ struct work_struct work;
+ #else /* OTG_TASKLET_WORK */
+ struct tasklet_struct tasklet;
+ #endif /* OTG_TASKLET_WORK */
+ BOOL terminated;
+ otg_tasklet_proc_t proc;
+ otg_tasklet_arg_t data;
+ BOOL debug;
+ otg_tag_t tag;
+ char *name;
+};
+
+/*! otg_tasklet_run - statr otg_tasklet process
+ * @param data - otg_tasklet pointer
+ */
+static inline void otg_tasklet_run(otg_tasklet_arg_t data)
+{
+ struct otg_tasklet *tasklet = (struct otg_tasklet *)data;
+
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ tasklet->proc(tasklet->data);
+ tasklet->terminated = TRUE;
+
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s finished\n", __FUNCTION__, tasklet->name);
+}
+
+/*! otg_tasklet_init
+ * Create otg task structure, create workqueue, initialize it.
+ * @param name - otg_tasklet name
+ * @param proc - otg_tasklet process
+ * @param data - otg_taskle data, argument for proc
+ * @param tag - otg_tasklet tag
+ * @return initialized otg_tasklet instance pointer
+ */
+static inline struct otg_tasklet *otg_tasklet_init(char *name, otg_tasklet_proc_t proc, otg_tasklet_arg_t data, otg_tag_t tag)
+{
+ struct otg_tasklet *tasklet;
+
+ //TRACE_STRING(tag, "INIT: %s", name);
+ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, name);
+
+ RETURN_NULL_UNLESS((tasklet = CKMALLOC(sizeof (struct otg_tasklet))));
+
+ tasklet->tag = tag;
+ tasklet->name = name;
+ tasklet->terminated = TRUE;
+ tasklet->proc = proc;
+ tasklet->data = data;
+
+ #ifdef OTG_TASKLET_WORK
+ INIT_WORK(&tasklet->work, otg_tasklet_run, tasklet);
+ #else /* OTG_TASKLET_WORK */
+ tasklet_init(&tasklet->tasklet, otg_tasklet_run, (otg_tasklet_arg_t) tasklet);
+ #endif /* OTG_TASKLET_WORK */
+
+ return tasklet;
+
+ CATCH(error) {
+ if (tasklet) LKFREE(tasklet);
+ return NULL;
+ }
+}
+
+/*! otg_tasklet_start
+ * Signal work task to re-start.
+ * @param tasklet - otg_tasklet instance pointer
+ */
+static void inline otg_tasklet_start(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "START: %s", name);
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ tasklet->terminated = FALSE;
+ #ifdef OTG_TASKLET_WORK
+ SCHEDULE_WORK(tasklet->work);
+ #else /* OTG_TASKLET_WORK */
+ tasklet_schedule(&tasklet->tasklet);
+ #endif /* OTG_TASKLET_WORK */
+}
+
+/*! otg_tasklet_exit
+ * Terminate and wait for otg task.
+ * @param tasklet - otg_tasklet instance pointer
+ */
+static void inline otg_tasklet_exit(struct otg_tasklet *tasklet)
+{
+ //TRACE_STRING(tag, "EXIT: %s", name);
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: %s\n", __FUNCTION__, tasklet->name);
+
+ #ifdef OTG_TASKLET_WORK
+ while (!tasklet->terminated) {
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: SLEEPING\n", __FUNCTION__);
+ otg_sleep( 1 );
+ if (tasklet->debug)
+ printk(KERN_INFO"%s: RUNNING\n", __FUNCTION__);
+ }
+ #else /* OTG_TASKLET_WORK */
+ tasklet_kill(&tasklet->tasklet);
+ #endif /* OTG_TASKLET_WORK */
+
+ LKFREE(tasklet);
+}
+
+
+/*! @} */
+
+/* ********************************************************************************************** */
+/*!
+ * @name DMA Cache
+ *
+ * @{
+ *
+ *
+ * #define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+ * #define CACHE_SYNC_TX(buf, len) consistent_sync (NULL, buf, len, PCI_DMA_TODEVICE)
+ */
+/*! @} */
+#define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+#if defined(CONFIG_OTG_NEW_TX_CACHE)
+#define CACHE_SYNC_TX(buf, len) consistent_sync (NULL, buf, len, PCI_DMA_TODEVICE)
+#else
+#define CACHE_SYNC_TX(buf, len) consistent_sync (buf, len, PCI_DMA_TODEVICE)
+#endif
+
+/*@}*/
+
+
+
+/* ********************************************************************************************** */
+/*! @name likely and unlikely
+ *
+ * Under linux these can be used to provide a hint to GCC that
+ * the expression being evaluated is probably going to evaluate
+ * to true (likely) or false (unlikely). The intention is that
+ * GCC can then provide optimal code for that.
+ * @{
+ */
+#ifndef likely
+#define likely(x) x
+#endif
+#define otg_likely(x) likely(x)
+
+#ifndef unlikely
+#define unlikely(x) x
+#endif
+#define otg_unlikely(x) unlikely(x)
+/* @} */
+
+
+
+
+/* ********************************************************************************************** */
+/*
+ * XXX Deprecated and/or move to platform or architecture specific
+ */
+
+
+
+// Common to all supported versions of Linux ??
+
+#if 1
+#include <linux/list.h>
+#define LIST_NODE struct list_head
+#define LIST_NODE_INIT(name) struct list_head name = {&name, &name}
+#define INIT_LIST_NODE(ptr) INIT_LIST_HEAD(ptr)
+#define LIST_ENTRY(pointer, type, member) list_entry(pointer, type, member)
+#define LIST_FOR_EACH(cursor, head) list_for_each(cursor, head)
+#define LIST_ADD_TAIL(n,h) list_add_tail(n,h)
+#define LIST_DEL(h) list_del(&(h))
+#else
+#include <otg/otg-list.h>
+#endif
+
+/*! @} */
+
+
+/*!@name Scheduling Primitives
+ *
+ * WORK_STRUCT
+ * WORK_ITEM
+ *
+ * SET_WORK_ARG()\n
+ * SCHEDULE_IMMEDIATE_WORK()\n
+ * NO_WORK_DATA()\n
+ * MOD_DEC_USE_COUNT\n
+ * MOD_INC_USE_COUNT\n
+ */
+
+/*! @{ */
+
+
+/* Separate Linux 2.4 and 2.6 versions of scheduling primitives */
+#if defined(LINUX26)
+ #include <linux/workqueue.h>
+
+ #define OLD_WORK_STRUCT work_struct
+ #define WORK_ITEM work_struct
+ #define OLD_WORK_ITEM work_struct
+ typedef struct OLD_WORK_ITEM OLD_WORK_ITEM;
+ #if 0
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) INIT_WORK((__item),(__routine),(__data))
+ #else
+ #include <linux/interrupt.h>
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) __prepare_work(&(__item),(__routine),(__data))
+ static inline void __prepare_work(struct work_struct *_work,
+ void (*_routine),
+ void * _data){
+ INIT_LIST_HEAD(&_work->entry);
+ _work->pending = 0;
+ _work->func = _routine;
+ _work->data = _data;
+ init_timer(&_work->timer);
+ }
+ #endif
+ //#undef PREPARE_WORK
+ typedef void (* WORK_PROC)(void *);
+
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+
+ #define SCHEDULE_DELAYED_WORK(item) schedule_delayed_work(&item, 0)
+ #define SCHEDULE_IMMEDIATE_WORK(item) SCHEDULE_WORK((item))
+ #define PENDING_WORK_ITEM(item) (item.pending != 0)
+ #define NO_WORK_DATA(item) (!item.data)
+ //#define _MOD_DEC_USE_COUNT //Not used in 2.6
+ //#define _MOD_INC_USE_COUNT //Not used in 2.6
+
+ //#define MODPARM(a) _##a
+
+ //#define MOD_PARM_BOOL(a, d, v) static int _##a = v; module_param_named(a, _##a, bool, 0); MODULE_PARM_DESC(a, d)
+ //#define MOD_PARM_INT(a, d, v) static int _##a = v; module_param_named(a, _##a, uint, 0); MODULE_PARM_DESC(a, d)
+ //#define MOD_PARM_STR(a, d, v) static char * _##a = v; module_param_named(a, _##a, charp, 0); MODULE_PARM_DESC(a, d)
+ #define OTG_INTERRUPT 0 /* SA_INTERRUPT in some environments */
+
+
+
+
+#else /* LINUX26 */
+
+ #define OLD_WORK_STRUCT tq_struct
+ #define OLD_WORK_ITEM tq_struct
+ typedef struct OLD_WORK_ITEM OLD_WORK_ITEM;
+ #define PREPARE_WORK_ITEM(item,work_routine,work_data) { item.routine = work_routine; item.data = work_data; }
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+ #define NO_WORK_DATA(item) (!(item).data)
+ #define PENDING_WORK_ITEM(item) (item.sync != 0)
+ //#define _MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+ //#define _MOD_INC_USE_COUNT MOD_INC_USE_COUNT
+
+ #define MODPARM(a) a
+
+ //#define MOD_PARM_BOOL(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ //#define MOD_PARM_INT(a, d, v) static int a = v; MOD_PARM(a, "i"); MOD_PARM_DESC(a, d);
+ //#define MOD_PARM_STR(a, d, v) static char * a = v; MOD_PARM(a, "s"); MOD_PARM_DESC(a, d);
+
+ typedef void (* WORK_PROC)(void *);
+
+ #define OTG_INTERRUPT 0 /* SA_INTERRUPT in some environments */
+#endif /* LINUX26 */
+
+/*! @} */
+
+
+/* ********************************************************************************************* */
+/* Linux specific - not part of otg-os.h implementation
+ */
+
+
+#if defined(LINUX26)
+/*! @name OTG Bus Type
+ */
+/*@{*/
+extern struct bus_type otg_bus_type;
+/*@}*/
+#endif /* defined(LINUX26) */
+
+
+
+#if defined(LINUX26)
+ #include <linux/gfp.h>
+#define GET_KERNEL_PAGE() __get_free_page(GFP_KERNEL)
+
+#else /* LINUX26 */
+
+ #include <linux/mm.h>
+ #define GET_KERNEL_PAGE() get_free_page(GFP_KERNEL)
+ #if !defined(IRQ_HANDLED)
+ // Irq's
+ typedef void irqreturn_t;
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #define IRQ_RETVAL(x)
+ #endif
+#endif /* LINUX26 */
+
+
+
+#endif /* defined(CONFIG_OTG_LNX) */
+#endif
diff --git a/drivers/otg/otgcore/otg-mesg-lnx.c b/drivers/otg/otgcore/otg-mesg-lnx.c
new file mode 100644
index 000000000000..254319396856
--- /dev/null
+++ b/drivers/otg/otgcore/otg-mesg-lnx.c
@@ -0,0 +1,519 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-mesg-l24.c - OTG state machine Administration
+ * @(#) tt@belcarra.com|otg/otgcore/otg-mesg-lnx.c|20070316085010|26362
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otgcore/otg-mesg-lnx.c
+ * @brief OTG Core Linux Message Interface.
+ *
+ * Implement linux char device via /proc/otg-mesg.
+ *
+ * @ingroup OTGMESG
+ * @ingroup LINUXOS
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+//#include <otg/otg-task.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-ocd.h>
+
+#include <linux/poll.h>
+#include <linux/sched.h>
+
+#if defined(LINUX24)
+#include <asm/div64.h>
+#endif
+
+
+wait_queue_head_t otg_message_queue;
+
+
+/*!
+ * otg_message_task() - Bottom half handler to process sent or received urbs.
+ *
+ * @param data pointer to parameters struct
+ */
+#if 0
+void otg_message_task(otg_task_arg_t data)
+{
+ struct otg_task * task = (struct otg_task *) data;
+
+ struct otg_instance *otg = (struct otg_instance *)task->data;
+
+ /* signal we have started */
+ otg_up_admin(task);
+
+ do {
+ /* wait for work */
+ TRACE_MSG0(CORE, "SLEEPING");
+ otg_down_work(task);
+ TRACE_MSG0(CORE, "WORKING");
+
+ wake_up_interruptible(&otg_message_queue);
+
+ } while (!task->terminate);
+
+
+ /* signal exiting */
+ task->terminated = TRUE;
+ otg_up_admin(task);
+}
+#endif
+void *otg_message_task(otg_task_arg_t data)
+{
+ struct otg_instance *otg = (struct otg_instance *)data;
+ wake_up_interruptible(&otg_message_queue);
+ return NULL;
+}
+
+
+#if 0
+/*!
+ * otg_message() - queue message data
+ * @param buf message to send.
+ */
+void otg_message (struct otg_instance *otg, char *buf)
+{
+ char time_buf[64];
+ static otg_tick_t save_ticks;
+ otg_tick_t new_ticks;
+ otg_tick_t ticks;
+
+ new_ticks = otg_tmr_ticks();
+
+ otg_write_message(otg, buf, strlen(buf));
+
+ ticks = otg_tmr_elapsed(&new_ticks, &save_ticks);
+ save_ticks = new_ticks;
+
+ if (ticks < 10000)
+ sprintf(time_buf, " %d uS\n", ticks);
+
+ else if (ticks < 10000000) {
+// do_div(ticks, 1000);
+ otg_do_div(ticks, 1000);
+ sprintf(time_buf, " %d mS\n", ticks);
+ }
+ else {
+// do_div(ticks, 1000000);
+ otg_do_div(ticks, 1000000);
+ sprintf(time_buf, " %d S\n", ticks);
+ }
+
+ otg_write_message(otg, time_buf, strlen(time_buf));
+
+ if (otg->message_task)
+ otg_up_work(otg->message_task);
+}
+#endif
+
+/*!
+ * otg_message_irq() - queue message data
+ * @param otg - otg instance pointer
+ * @param buf message to send.
+ */
+void otg_message_irq(struct otg_instance *otg, char *buf)
+{
+ otg_message(otg, buf);
+}
+
+/*!
+ * otg_message_block() - implement block
+ */
+unsigned int otg_message_block(void)
+{
+ int count = otg_data_queued();
+ if (count) return count;
+ interruptible_sleep_on(&otg_message_queue);
+ return otg_data_queued();
+}
+
+/*!
+ * otg_message_poll() - implement poll
+ * @param filp file pointer
+ * @param wait poll table structure
+ */
+unsigned int otg_message_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ //TRACE_MSG0(CORE, "polling ");
+ poll_wait(filp, &otg_message_queue, wait);
+ return otg_data_queued() ? POLLIN | POLLRDNORM : 0;
+}
+
+/*!
+ * otg_set_usbd_ops() - connect usbd operations to otg instance
+ *
+ * @param data - usbd_ops
+ * @return 0 on finish
+ */
+struct usbd_ops *otg_usbd_ops;
+int otg_set_usbd_ops(void *data)
+{
+ struct usbd_ops *usbd_ops = (struct usbd_ops *)data;
+ otg_usbd_ops = usbd_ops;
+ return 0;
+}
+
+
+
+/*!
+ * otg_message_ioctl_internal() - ioctl call
+ * @param cmd ioctl command.
+ * @param arg ioctl arguement.
+ * @return non-zero for error.
+ */
+int otg_message_ioctl_internal(unsigned int cmd, unsigned long arg)
+{
+ int i;
+ int len;
+ int flag;
+ struct otg_admin_command admin_command;
+ struct otg_status_update status_update;
+ struct otg_firmware_info firmware_info;
+ struct otg_state otg_state;
+ struct otg_test otg_test;
+ struct otg_ioctl_name *otg_ioctl_name;
+ static char func_buf[32];
+ char *sp, *dp;
+ char *name;
+
+ //TRACE_MSG2(CORE, "cmd: %08x %08x", cmd, _IOC_NR(cmd));
+
+ switch (_IOC_DIR(cmd)) {
+ case _IOC_NONE:
+ switch (_IOC_NR(cmd)) {
+ }
+ break;
+
+ case _IOC_WRITE:
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(OTGADMIN_SET_FUNCTION):
+ TRACE_MSG0(CORE, "OTGADMIN_SET_FUNCTION");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ memset(&admin_command, 0x41, sizeof(struct otg_admin_command));
+ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd)));
+ len = sizeof(mesg_otg_instance->function_name);
+ admin_command.string[len] = '\0';
+ TRACE_MSG1(CORE, "Setting function: \"%s\"", mesg_otg_instance->function_name);
+ strncpy(mesg_otg_instance->function_name, admin_command.string, len);
+ mesg_otg_instance->function_name[len-1] = '\0';
+#if defined(LINUX26)
+ {
+ char *cp = mesg_otg_instance->function_name;
+ TRACE_MSG8(CORE, "function_name: %c%c%c%c%c%c%c%c",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+
+ }
+#endif
+ return 0;
+
+ case _IOC_NR(OTGADMIN_SET_SERIAL):
+ TRACE_MSG0(CORE, "OTGADMIN_SET_SERIAL");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd)));
+ admin_command.string[sizeof(admin_command.string) - 1] = '\0';
+ for (sp = admin_command.string, dp = mesg_otg_instance->serial_number, i = 0;
+ *sp && (i < (sizeof(admin_command.string) - 1)); i++, sp++)
+ if (isxdigit(*sp)) *dp++ = toupper(*sp);
+
+ *sp = '\0';
+ //TRACE_MSG1(CORE, "serial_number: %s", mesg_otg_instance->serial_number);
+ return 0;
+
+ case _IOC_NR(OTGADMIN_SET_INFO):
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO");
+ memset(&firmware_info, 0x41, sizeof(firmware_info));
+ RETURN_EINVAL_IF(copy_from_user(&firmware_info, (void *)arg, _IOC_SIZE(cmd)));
+
+
+ return otg_mesg_set_firmware_info(&firmware_info);
+ return 0;
+
+ case _IOC_NR(OTGADMIN_SET_STATE):
+ //TRACE_MSG0(CORE, "OTGADMIN_XXX_STATE");
+ RETURN_EINVAL_IF(copy_from_user(&otg_state, (void *)arg, _IOC_SIZE(cmd)));
+
+ RETURN_EINVAL_UNLESS(otg_firmware_loading);
+ //TRACE_MSG0(CORE, "OTGADMIN_SET_STATE");
+ RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loading->number_of_states);
+ memcpy(otg_firmware_loading->otg_states + otg_state.state, &otg_state, sizeof(otg_state));
+ return 0;
+
+ case _IOC_NR(OTGADMIN_SET_TEST):
+ //TRACE_MSG0(CORE, "OTGADMIN_GET/SET");
+ RETURN_EINVAL_IF(copy_from_user(&otg_test, (void *)arg, _IOC_SIZE(cmd)));
+
+ RETURN_EINVAL_UNLESS(otg_firmware_loading);
+ //TRACE_MSG1(CORE, "OTGADMIN_SET_TEST : %d", otg_test.test);
+ RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loading->number_of_tests);
+ memcpy(otg_firmware_loading->otg_tests + otg_test.test, &otg_test, sizeof(otg_test));
+ return 0;
+ }
+ break;
+
+ case _IOC_READ:
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(OTGADMIN_GET_FUNCTION):
+ TRACE_MSG0(CORE, "OTGADMIN_GET_FUNCTION");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ RETURN_EINVAL_IF(copy_from_user(&admin_command, (void *)arg, _IOC_SIZE(cmd)));
+ name = otg_usbd_ops->function_name(admin_command.n);
+ admin_command.string[0] = '\0';
+ if (name)
+ strncat(admin_command.string, name, sizeof(admin_command.string));
+
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd)));
+ return 0;
+
+ case _IOC_NR(OTGADMIN_GET_SERIAL):
+ TRACE_MSG0(CORE, "OTGADMIN_GET_SERIAL");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ strncpy(admin_command.string, mesg_otg_instance->serial_number, sizeof(admin_command.string));
+ admin_command.string[sizeof(admin_command.string) - 1] = '\0';
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &admin_command, _IOC_SIZE(cmd)));
+ return 0;
+
+ case _IOC_NR(OTGADMIN_STATUS):
+ //TRACE_MSG0(CORE, "OTGADMIN_STATUS");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ memset(&status_update, 0, sizeof(struct otg_status_update));
+
+ otg_mesg_get_status_update(&status_update);
+
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &status_update, _IOC_SIZE(cmd)));
+ return 0;
+
+ case _IOC_NR(OTGADMIN_GET_INFO):
+ TRACE_MSG0(CORE, "OTGADMIN_GET_INFO");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+
+ otg_mesg_get_firmware_info(&firmware_info);
+
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &firmware_info, _IOC_SIZE(cmd)));
+ //TRACE_MSG0(CORE, "OTGADMIN_GET_INFO: finished");
+ return 0;
+
+ case _IOC_NR(OTGADMIN_GET_STATE):
+ //TRACE_MSG0(CORE, "OTGADMIN_XXX_STATE");
+ RETURN_EINVAL_IF(copy_from_user(&otg_state, (void *)arg, _IOC_SIZE(cmd)));
+
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ //TRACE_MSG0(CORE, "OTGADMIN_GET_STATE");
+ RETURN_EINVAL_UNLESS (otg_state.state < otg_firmware_loaded->number_of_states);
+ memcpy(&otg_state, otg_firmware_loaded->otg_states + otg_state.state, sizeof(otg_state));
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_state, _IOC_SIZE(cmd)));
+ return 0;
+
+ case _IOC_NR(OTGADMIN_GET_TEST):
+ //TRACE_MSG0(CORE, "OTGADMIN_GET/SET");
+ RETURN_EINVAL_IF(copy_from_user(&otg_test, (void *)arg, _IOC_SIZE(cmd)));
+
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ //TRACE_MSG1(CORE, "OTGADMIN_GET_TEST: %d", otg_test.test);
+ RETURN_EINVAL_UNLESS (otg_test.test < otg_firmware_loaded->number_of_tests);
+ memcpy(&otg_test, otg_firmware_loaded->otg_tests + otg_test.test, sizeof(otg_test));
+ RETURN_EINVAL_IF(copy_to_user((void *)arg, &otg_test, _IOC_SIZE(cmd)));
+ return 0;
+ }
+ break;
+ }
+
+ TRACE_MSG0(CORE, "OTGADMIN_");
+ RETURN_EINVAL_UNLESS(otg_firmware_loaded);
+ for (otg_ioctl_name = otg_ioctl_names; otg_ioctl_name && otg_ioctl_name->cmd; otg_ioctl_name++) {
+ //TRACE_MSG4(CORE, "lookup: %04x %04x %08x %08x",
+ // _IOC_NR(cmd), _IOC_NR(otg_ioctl_name->cmd), cmd, otg_ioctl_name->cmd);
+ BREAK_IF(_IOC_NR(otg_ioctl_name->cmd) == _IOC_NR(cmd));
+ }
+ TRACE_MSG3(CORE, "checking %d %08x %08x", _IOC_NR(cmd), otg_ioctl_name->cmd, cmd);
+ RETURN_EINVAL_UNLESS(otg_ioctl_name->cmd);
+ __get_user(flag, (int *)arg);
+ TRACE_MSG3(CORE, "%s %08x flag: %d", otg_ioctl_name->name, otg_ioctl_name->set, flag);
+ otg_event (mesg_otg_instance, flag ? otg_ioctl_name->set : _NOT(otg_ioctl_name->set), CORE, otg_ioctl_name->name);
+ return 0;
+}
+
+
+
+/*!
+ * otg_message_ioctl() - ioctl
+ * @param inode inode structure.
+ * @param filp file.
+ * @param cmd ioctl command.
+ * @param arg ioctl argument.
+ * @return non-zero for error.
+ */
+int otg_message_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int i;
+ int len;
+ int flag;
+ struct otg_admin_command admin_command;
+ struct otg_status_update status_update;
+ struct otg_firmware_info firmware_info;
+ struct otg_state otg_state;
+ struct otg_test otg_test;
+ //struct otg_ioctl_map *map = ioctl_map;
+ struct otg_ioctl_name *otg_ioctl_name;
+
+ static char func_buf[32];
+
+ //TRACE_MSG6(CORE, "cmd: %08x arg: %08x type: %02d nr: %02d dir: %02d size: %02d",
+ // cmd, arg, _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_DIR(cmd), _IOC_SIZE(cmd));
+
+ RETURN_EINVAL_UNLESS (_IOC_TYPE(cmd) == OTGADMIN_MAGIC);
+ RETURN_EINVAL_UNLESS (_IOC_NR(cmd) <= OTGADMIN_MAXNR);
+
+ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_READ) && !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)));
+ RETURN_EFAULT_IF((_IOC_DIR(cmd) == _IOC_WRITE) && !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)));
+
+ return otg_message_ioctl_internal(cmd, arg);
+}
+
+
+/*!
+ * otg_message_proc_read() - implement proc file system read.
+ * Standard proc file system read function.
+ * @param file file.
+ * @param buf buffer to fill
+ * @param count size of buffer
+ * @param pos file position
+ * @return number of bytes or negative number for error
+ */
+static ssize_t otg_message_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0, avail;
+ char *bp,*ep;
+ struct list_head *lhd;
+ int state;
+
+ // get a page, max 4095 bytes of data...
+ RETURN_ENOMEM_UNLESS ((page = GET_KERNEL_PAGE()));
+
+ bp = (char *) page;
+
+ len = bp - (char *) page;
+
+ if (( avail = otg_message_block())) {
+
+ len += otg_read_message(bp, 4095);
+
+ if (len > count)
+ len = -EINVAL;
+ else if ((len > 0) && copy_to_user (buf, (char *) page, len))
+ len = -EFAULT;
+
+ }
+
+ free_page (page);
+ return len;
+}
+
+/*! otg_message_proc_ioctl() -
+ * @param inode inode structure.
+ * @param filp file.
+ * @param cmd ioctl command.
+ * @param arg ioctl argument.
+ * @return non-zero for error.
+ */
+int otg_message_proc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ return otg_message_ioctl(inode, filp, cmd, arg);
+}
+
+/*!
+ * @param filp file.
+ * @param wait buffer to fill
+ * @return number of bytes or negative number for error
+ */
+unsigned int otg_message_proc_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ return otg_message_poll(filp, wait);
+}
+
+/*!
+ * @var otg_message_proc_switch_functions
+ */
+static struct file_operations otg_message_proc_switch_functions = {
+ ioctl:otg_message_proc_ioctl,
+ poll:otg_message_proc_poll,
+ read:otg_message_proc_read,
+};
+
+
+/*!
+ * otg_message_init_l24() - initialize
+ * @param otg - otg instance pointer
+ */
+int otg_message_init_l24(struct otg_instance *otg)
+{
+ struct proc_dir_entry *message = NULL;
+ init_waitqueue_head(&otg_message_queue);
+ RETURN_EINVAL_UNLESS((otg->message_task = otg_task_init2("otgmessage", otg_message_task, (void *) otg, CORE)));
+
+ //otg->message_task->debug = TRUE;
+
+ otg_task_start(otg->message_task);
+
+ THROW_IF (!(message = create_proc_entry ("otg_message", 0666, 0)), error);
+ message->proc_fops = &otg_message_proc_switch_functions;
+ CATCH(error) {
+ if (message)
+ remove_proc_entry("otg_message", NULL);
+ if (otg->message_task) {
+ otg_task_exit(otg->message_task);
+ otg->message_task = NULL;
+ }
+
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+/*!
+ * otg_message_exit_l24() - exit
+ * @param otg - otg instance pointer
+ */
+void otg_message_exit_l24(struct otg_instance *otg)
+{
+ remove_proc_entry("otg_message", NULL);
+ otg_task_exit(otg->message_task);
+ otg->message_task = NULL;
+}
+
+OTG_EXPORT_SYMBOL(otg_message);
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/otgcore/otg-mesg.c b/drivers/otg/otgcore/otg-mesg.c
new file mode 100644
index 000000000000..d8ceeaceb9c4
--- /dev/null
+++ b/drivers/otg/otgcore/otg-mesg.c
@@ -0,0 +1,416 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-mesg.c - OTG state machine Administration
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/otg-mesg.c|20061220065259|48878
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otgcore/otg-mesg.c
+ * @brief OTG Core Message Facility.
+ *
+ * The OTG Core Message Facility has two functions:
+ *
+ * 1. implement a message queue for messages from the otg state machine to the otg
+ * management application
+ *
+ * 2. implement an ioctl control mechanism allowing the otg managment application
+ * to pass data and commands to the otg state machine
+ *
+ * @ingroup OTGMESG
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-ocd.h>
+
+//#if defined(OTG_LINUX)
+//#include <linux/poll.h>
+//#include <linux/sched.h>
+//#endif /* defined(OTG_LINUX) */
+
+//#define OTG_MESSAGE_BUF_SIZE 512
+#define OTG_MESSAGE_BUF_SIZE 2048
+
+struct otg_instance * mesg_otg_instance;
+
+char *otg_message_buf; // buffer
+char *otg_message_head; // next available empty space
+char *otg_message_tail; // next available data if not equal to head
+
+/*!
+ * otg_data_avail() - amount of data in buffer
+ * space data
+ * tp hp 20 - (10 - 5) = 15 (10 - 5)
+ * 0......5.data.10......20
+ *
+ * hp tp (10 - 5) = 5 20 - (10 - 5)
+ * 0.data.5......10.data.20
+ *
+ * @return Bytes available in message ring buffer.
+ */
+int otg_data_avail(void)
+{
+ int avail = (otg_message_head >= otg_message_tail) ?
+ (otg_message_head - otg_message_tail) :
+ OTG_MESSAGE_BUF_SIZE - (otg_message_tail - otg_message_head) ;
+
+ //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p data avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail);
+ return avail;
+}
+
+/*!
+ * otg_space_avail() - amount of space in buffer
+ *
+ * @return Space available in message ring buffer.
+ */
+int otg_space_avail(void)
+{
+ int avail = (otg_message_head >= otg_message_tail) ?
+ OTG_MESSAGE_BUF_SIZE - (otg_message_head - otg_message_tail) :
+ (otg_message_tail - otg_message_head) ;
+
+ //TRACE_MSG4(CORE, "buf: %p head: %p tail: %p space avail: %d", otg_message_buf, otg_message_head, otg_message_tail, avail);
+ return avail;
+}
+
+/*!
+ * otg_get_byte() - return next byte
+ *
+ * @return Next byte in message ring buffer
+ */
+char otg_get_byte(void)
+{
+ char c = *otg_message_tail;
+
+ //TRACE_MSG3(CORE, "buf: %p head: %p tail: %p", otg_message_buf, otg_message_head, otg_message_tail);
+ if ((otg_message_tail - otg_message_buf) == OTG_MESSAGE_BUF_SIZE)
+ otg_message_tail = otg_message_buf;
+ else
+ otg_message_tail++;
+
+ return c;
+}
+
+/*!
+ * otg_put_byte() - put byte into buffer
+ *
+ * Put byte into message ring buffer
+ *
+ * @param byte put into message ring.
+ */
+void otg_put_byte(char byte)
+{
+ *otg_message_head = byte;
+ if ((otg_message_head - otg_message_buf) == OTG_MESSAGE_BUF_SIZE)
+ otg_message_head = otg_message_buf;
+ else
+ otg_message_head++;
+
+}
+
+/*!
+ * otg_write_message_irq() - queue message data (interrupt version)
+ *
+ * Attempt to write message data into ring buffer, return amount
+ * of data actually written.
+ * @param otg - otg instance pointer
+ * @param buf - pointer to data
+ * @param size - amount of available data
+ * @return number of bytes that where not written.
+ */
+static int otg_write_message_irq (struct otg_instance *otg, char *buf, int size)
+{
+ struct pcd_instance *pcd = NULL;
+ struct usbd_bus_instance *bus = NULL;
+
+ pcd = (struct pcd_instance *)mesg_otg_instance->pcd;
+ if (pcd)
+ bus = pcd->bus;
+
+ if (size >= (OTG_MESSAGE_BUF_SIZE - 2))
+ return size;
+
+ while (otg_space_avail() < (size + 1))
+ otg_get_byte();
+
+
+ /* copy, note that we don't have to test for space as we have
+ * already ensured that there is enough room for all of the data
+ */
+ while (size--)
+ otg_put_byte(*buf++);
+
+ //otg_put_byte('\n');
+
+ return size;
+}
+
+/*!
+ * otg_write_message() - queue message data
+ *
+ * Attempt to write message data into ring buffer, return amount
+ * of data actually written.
+ * @param otg - otg instance pointer
+ * @param buf - pointer to data
+ * @param size - amount of available data
+ * @return number of bytes that where not written.
+ */
+int otg_write_message (struct otg_instance *otg, char *buf, int size)
+{
+ int rc;
+ otg_pthread_mutex_lock(&mesg_otg_instance->mutex); /* lock mutex */
+ rc = otg_write_message_irq(otg, buf, size);
+ otg_pthread_mutex_unlock(&mesg_otg_instance->mutex); /* unlock mutex */
+ return size;
+}
+
+/*!
+ * otg_message() - queue message data
+ * @param otg pointer to otg instance
+ * @param buf message to send.
+ */
+void otg_message (struct otg_instance *otg, char *buf)
+{
+ char time_buf[64];
+ static otg_tick_t save_ticks;
+ otg_tick_t new_ticks;
+ otg_tick_t ticks;
+
+ new_ticks = otg_tmr_ticks();
+
+ otg_write_message(otg, buf, strlen(buf));
+
+ ticks = otg_tmr_elapsed(&new_ticks, &save_ticks);
+ save_ticks = new_ticks;
+
+ if (ticks < 10000)
+ sprintf(time_buf, " %ld uS\n", ticks);
+
+ else if (ticks < 10000000) {
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000);
+ sprintf(time_buf, " %ld mS\n", ticks / 1000);
+ }
+ else {
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000000);
+ sprintf(time_buf, " %ld S\n", ticks / 1000000);
+ }
+
+ //TRACE_MSG3(CORE, "ticks: new_ticks: %d - save_ticks: %d = ticks: %d ", new_ticks, save_ticks, ticks);
+ //TRACE_STRING(CORE, "time_buf: %s", time_buf);
+
+ otg_write_message(otg, time_buf, strlen(time_buf));
+
+ if (otg->message_task)
+ otg_up_work(otg->message_task);
+}
+
+
+/*!
+ * otg_read_message() - get queued message data
+ *
+ * @param buf - pointer to data
+ * @param size - amount of available data
+ * @return the number of bytes read.
+ */
+int otg_read_message(char *buf, int size)
+{
+ int count = 0;
+ otg_pthread_mutex_lock(&mesg_otg_instance->mutex); /* lock mutex */
+
+ /* Copy bytes to the buffer while there is data available and space
+ * to put it in the buffer
+ */
+ for ( ; otg_data_avail() && size--; count++)
+ BREAK_IF((*buf++ = otg_get_byte()) == '\n');
+
+ otg_pthread_mutex_unlock(&mesg_otg_instance->mutex); /* unlock mutex */
+ return count;
+}
+
+/*!
+ * otg_data_queued() - return amount of data currently queued
+ *
+ * @return Amount of data currently queued in mesage ring buffer.
+ */
+int otg_data_queued(void)
+{
+ int count;
+ otg_pthread_mutex_lock(&mesg_otg_instance->mutex); /* lock mutex */
+ count = otg_data_avail();
+ otg_pthread_mutex_unlock(&mesg_otg_instance->mutex); /* unlock mutex */
+ return count;
+}
+
+
+/*!
+ * otg_message_init() - initialize
+ * @param otg OTG instance
+ */
+int otg_message_init(struct otg_instance *otg)
+{
+ //struct proc_dir_entry *message = NULL;
+ otg_message_buf = otg_message_head = otg_message_tail = NULL;
+ THROW_UNLESS((otg_message_buf = CKMALLOC(OTG_MESSAGE_BUF_SIZE + 16)), error);
+ otg_message_head = otg_message_tail = otg_message_buf ;
+
+ mesg_otg_instance = otg;
+ CATCH(error) {
+ #if defined(OTG_LINUX)
+ #endif /* defined(OTG_LINUX) */
+ #if defined(OTG_WINCE)
+ DEBUGMSG(ZONE_INIT, (_T("otg_message_init: creating //proc//otg_message failed\n")) );
+ #endif /* defined(OTG_LINUX) */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*!
+ * otg_message_exit() - exit
+ */
+void otg_message_exit(void)
+{
+ mesg_otg_instance = NULL;
+ if (otg_message_buf)
+ LKFREE(otg_message_buf);
+ otg_message_buf = otg_message_head = otg_message_tail = NULL;
+}
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * otg_mesg_get_status_update() - get status update
+ * @param status_update
+ */
+void otg_mesg_get_status_update(struct otg_status_update *status_update)
+{
+
+ //TRACE_MSG0(CORE, "--");
+ status_update->state = mesg_otg_instance->state;
+ status_update->meta = mesg_otg_instance->current_outputs->meta;
+ status_update->inputs = mesg_otg_instance->current_inputs;
+ status_update->outputs = mesg_otg_instance->outputs;
+ status_update->capabilities = 0;
+
+ strncpy(status_update->fw_name, otg_firmware_loaded->fw_name, OTGADMIN_MAXSTR);
+ strncpy(status_update->state_name, otg_get_state_name(mesg_otg_instance->state), OTGADMIN_MAXSTR);
+
+ if (mesg_otg_instance->function_name)
+ strncpy(status_update->function_name, mesg_otg_instance->function_name, OTGADMIN_MAXSTR);
+
+ TRACE_MSG5(CORE, "state: %02x meta: %02x inputs: %08x outputs: %04x",
+ status_update->state, status_update->meta, status_update->inputs,
+ status_update->outputs, status_update->capabilities);
+}
+
+
+/*!
+ * otg_mesg_get_firmware_info() - get firmware info
+ * @param firmware_info
+ */
+void otg_mesg_get_firmware_info(struct otg_firmware_info *firmware_info)
+{
+ memset(firmware_info, 0, sizeof(firmware_info));
+ // XXX irq lock
+
+ firmware_info->number_of_states = otg_firmware_loaded->number_of_states;
+ firmware_info->number_of_tests = otg_firmware_loaded->number_of_tests;
+ memcpy(&firmware_info->fw_name, otg_firmware_loaded->fw_name, sizeof(firmware_info->fw_name));
+
+}
+
+/*!
+ * otg_mesg_set_firmware_info() - set firmware info
+ * @param firmware_info
+ */
+int otg_mesg_set_firmware_info(struct otg_firmware_info *firmware_info)
+{
+
+ if (otg_firmware_loading) {
+
+ if ( (otg_firmware_loading->number_of_states == firmware_info->number_of_states) &&
+ (otg_firmware_loading->number_of_tests == firmware_info->number_of_tests)
+ ) {
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: loaded");
+ otg_firmware_loaded = otg_firmware_loading;
+ otg_firmware_loading = NULL;
+ return 0;
+ }
+ else {
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: abandoned");
+ LKFREE(otg_firmware_loading->otg_states);
+ LKFREE(otg_firmware_loading->otg_tests);
+ LKFREE(otg_firmware_loading);
+ return -EINVAL;
+ }
+ }
+
+ TRACE_MSG2(CORE, "OTGADMIN_SET_INFO: otg_firmware_loaded: %x otg_firmware_orig: %x",
+ otg_firmware_loaded, otg_firmware_orig);
+ if (otg_firmware_loaded && (otg_firmware_loaded != otg_firmware_orig)) {
+ LKFREE(otg_firmware_loaded->otg_states);
+ LKFREE(otg_firmware_loaded->otg_tests);
+ LKFREE(otg_firmware_loaded);
+ otg_firmware_loaded = otg_firmware_orig;
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: unloaded");
+ }
+ if (firmware_info->number_of_states && firmware_info->number_of_tests) {
+ int size;
+ otg_firmware_loaded = NULL;
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup");
+ THROW_UNLESS((otg_firmware_loading = CKMALLOC(sizeof(struct otg_firmware))), error);
+ size = sizeof(struct otg_state) * firmware_info->number_of_states;
+ THROW_UNLESS((otg_firmware_loading->otg_states = CKMALLOC(size)), error);
+ size = sizeof(struct otg_test) * firmware_info->number_of_tests;
+ THROW_UNLESS((otg_firmware_loading->otg_tests = CKMALLOC(size)), error);
+
+ otg_firmware_loading->number_of_states = firmware_info->number_of_states;
+ otg_firmware_loading->number_of_tests = firmware_info->number_of_tests;
+ memcpy(otg_firmware_loading->fw_name, &firmware_info->fw_name, sizeof(firmware_info->fw_name));
+
+ CATCH(error) {
+ TRACE_MSG0(CORE, "OTGADMIN_SET_INFO: setup error");
+ if (otg_firmware_loading) {
+ if (otg_firmware_loading->otg_states) LKFREE(otg_firmware_loading->otg_states);
+ if (otg_firmware_loading->otg_tests) LKFREE(otg_firmware_loading->otg_tests);
+ LKFREE(otg_firmware_loading);
+ return -EINVAL;
+ }
+ }
+
+ }
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
diff --git a/drivers/otg/otgcore/otg-trace-lnx.c b/drivers/otg/otgcore/otg-trace-lnx.c
new file mode 100644
index 000000000000..ff2ac93e2177
--- /dev/null
+++ b/drivers/otg/otgcore/otg-trace-lnx.c
@@ -0,0 +1,318 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-trace-l24.c
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/otg-trace-lnx.c|20061218212925|12732
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ *
+ */
+/*!
+ * @file otg/otgcore/otg-trace-lnx.c
+ * @brief OTG Core Linux Debug Trace Facility
+ *
+ * This allows access to the OTG Core Trace via /proc/trace_otg. Each time
+ * this file is opened and read it will contain a snapshot of the most
+ * recent trace messages.
+ *
+ * Reading the trace resets it, the next read will open a new snapshot.
+ *
+ * N.B. There is no protection from multiple reads.
+ *
+ * N.B. This is for debugging and is not normally enabled for production software.
+ *
+ * @ingroup OTGTRACE
+ * @ingroup LINUXOS
+ */
+
+#include <otg/otg-compat.h>
+#if defined(CONFIG_OTG_LNX) || defined(_OTG_DOXYGEN)
+
+#include <otg/otg-module.h>
+#include <linux/vmalloc.h>
+#include <stdarg.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) || defined(_OTG_DOXYGEN)
+
+
+extern otg_trace_snapshot otg_snapshot1;
+extern otg_trace_snapshot otg_snapshot2;
+
+extern int otg_trace_exiting;
+extern otg_trace_snapshot *otg_trace_current_traces;
+extern otg_trace_snapshot *otg_trace_other_traces;
+extern otg_pthread_mutex_t otg_trace_mutex;
+
+
+
+
+/* *
+ * otg_trace_proc_read_l24 - implement proc file system read.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Standard proc file system read function.
+ */
+static ssize_t otg_trace_proc_read_l24 (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ int index;
+ int oindex;
+ int rc;
+
+ otg_tick_t ticks = 0;
+
+ unsigned char *cp;
+ int skip = 0;
+
+ otg_trace_t px;
+ otg_trace_t ox;
+ otg_trace_t vx;
+ otg_trace_t *o, *p;
+
+ RETURN_ENOMEM_IF (!(page = GET_KERNEL_PAGE()));
+
+ //_MOD_INC_USE_COUNT;
+
+ // Lock out tag changes while reading
+ //DOWN(&otg_trace_sem_l24);
+ //while(otg_sem_wait(&otg_trace_sem_l24));
+ len = otg_trace_proc_read((char *)page, count, (int *)pos);
+
+ if (len > count) {
+ //printk(KERN_INFO"%s: EINVAL\n", __FUNCTION__);
+ len = -EINVAL;
+ }
+
+ else if (len > 0 && copy_to_user (buf, (char *) page, len)) {
+
+ printk(KERN_INFO"%s: EFAULT\n", __FUNCTION__);
+ len = -EFAULT;
+ }
+
+ //free_page (page);
+ //UP(&otg_trace_sem_l24);
+ //otg_sem_post(&otg_trace_sem_l24);
+ free_page (page);
+ //_MOD_DEC_USE_COUNT;
+ return len;
+}
+
+#define MAX_TRACE_CMD_LEN 64
+#if 1
+/*! otg_trace_proc_write - implement proc file system write.
+ * @param buf - command message buffer
+ * @param count- message length
+ * @param pos - trace slot to write to
+ * @return count
+ *
+ */
+int otg_trace_proc_write (const char *buf, int count, int * pos)
+{
+#define MAX_TRACE_CMD_LEN 64
+ char command[MAX_TRACE_CMD_LEN+1];
+ size_t n = count;
+ size_t l;
+ char c;
+
+ if (n > 0) {
+ l = MIN(n,MAX_TRACE_CMD_LEN);
+ if (copy_from_user (command, buf, l))
+ count = -EFAULT;
+ else {
+ n -= l;
+ while (n > 0) {
+ if (copy_from_user (&c, buf + (count - n), 1)) {
+ count = -EFAULT;
+ break;
+ }
+ n -= 1;
+ }
+ // Terminate command[]
+ if (l > 0 && command[l-1] == '\n') {
+ l -= 1;
+ }
+ command[l] = 0;
+ }
+ }
+
+ if (0 >= count) {
+ return count;
+ }
+
+
+ return count;
+}
+#endif
+/* *
+ * otg_trace_proc_write_l24 - implement proc file system write.
+ * @file
+ * @buf
+ * @count
+ * @pos
+ *
+ * Proc file system write function.
+ */
+static ssize_t otg_trace_proc_write_l24 (struct file *file, const char *buf, size_t count, loff_t * pos)
+{
+ char command[MAX_TRACE_CMD_LEN+1];
+ size_t l = MIN(count, MAX_TRACE_CMD_LEN);
+
+ RETURN_ZERO_UNLESS(count);
+ RETURN_EINVAL_IF (copy_from_user (command, buf, l));
+
+
+ RETURN_EINVAL_IF(copy_from_user (command, buf, l));
+
+ return otg_trace_proc_write(command, l, (int *)pos);
+#if 0
+ else {
+ n -= l;
+ while (n > 0) {
+ if (copy_from_user (&c, buf + (count - n), 1)) {
+ count = -EFAULT;
+ break;
+ }
+ n -= 1;
+ }
+ // Terminate command[]
+ if (l > 0 && command[l-1] == '\n') {
+ l -= 1;
+ }
+ command[l] = 0;
+ }
+#endif
+}
+
+/* Module init ************************************************************** */
+
+
+
+static struct file_operations otg_trace_proc_operations_functions = {
+ read: otg_trace_proc_read_l24,
+ write: otg_trace_proc_write_l24,
+};
+
+
+int otg_trace_modinit(void);
+void otg_trace_modexit(void);
+void otg_trace_init_snapshot(otg_trace_snapshot *tss);
+
+otg_trace_snapshot *test;
+
+/*! rel_snapshot - release trace snapshot
+ *
+ * @param tss - pointer to otg_trace_snapshot
+ * @return NULL
+ */
+
+otg_trace_snapshot *rel_snapshot_lnx(otg_trace_snapshot *tss)
+{
+ RETURN_NULL_UNLESS(tss);
+ if (tss->traces) {
+ vfree(tss->traces);
+ tss->traces = NULL;
+ }
+ return NULL;
+}
+
+/*! otg_trace_snapshot - allocate otg trace snapshot memory resource
+ *
+ * @param tss - pointer to otg_trace_snapshot
+ * @return otg_trace_snapshot point on success
+ */
+
+otg_trace_snapshot *get_snapshot_lnx(otg_trace_snapshot *tss)
+{
+ UNLESS ((tss->traces = vmalloc(sizeof(otg_trace_t) * TRACE_MAX))) {
+// printk(KERN_ERR "Cannot allocate memory for OTG trace log.\n");
+ return rel_snapshot_lnx(tss);
+ }
+ otg_trace_init_snapshot(tss);
+ return tss;
+}
+
+
+/*! otg_trace_modinit_lnx
+ *
+ * Return non-zero if not successful.
+ */
+int otg_trace_modinit_lnx (void)
+{
+ struct proc_dir_entry *p;
+
+ otg_trace_current_traces = otg_trace_other_traces = NULL;
+
+ UNLESS ((otg_trace_current_traces = get_snapshot_lnx(&otg_snapshot1)) &&
+ (otg_trace_other_traces = get_snapshot_lnx(&otg_snapshot2)))
+ {
+ printk(KERN_INFO"%s: ERROR\n", __FUNCTION__);
+ otg_trace_current_traces = rel_snapshot_lnx(otg_trace_current_traces);
+ otg_trace_other_traces = rel_snapshot_lnx(otg_trace_other_traces);
+ return -ENOMEM;
+ }
+
+ RETURN_EINVAL_IF(otg_trace_modinit());
+
+ //RETURN_EINVAL_IF(otg_sem_init_unlocked((&otg_trace_sem_l24)));
+
+ // create proc filesystem entries
+ if ((p = create_proc_entry ("trace_otg", 0, 0)) == NULL)
+ printk(KERN_INFO"%s PROC FS failed\n", "trace_otg");
+ else
+ p->proc_fops = &otg_trace_proc_operations_functions;
+
+ return 0;
+}
+
+/*! otg_trace_modexit_lnx - remove procfs entry, free trace data space.
+ */
+void otg_trace_modexit_lnx (void)
+{
+ otg_trace_t *p;
+ unsigned long flags;
+ otg_trace_snapshot *c,*o;
+
+ remove_proc_entry ("trace_otg", NULL);
+
+ c = otg_trace_current_traces;
+ o = otg_trace_other_traces;
+
+ otg_trace_modexit();
+
+ rel_snapshot_lnx(c);
+ rel_snapshot_lnx(o);
+}
+
+#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+//int otg_trace_init_l24 (void) {return 0;}
+//void otg_trace_exit_l24 (void) {}
+void otg_trace_modexit_lnx (void) {}
+int otg_trace_modinit_lnx (void) {return 0;}
+#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+
+#endif /* defined(CONFIG_OTG_LNX) */
diff --git a/drivers/otg/otgcore/otg-trace.c b/drivers/otg/otgcore/otg-trace.c
new file mode 100644
index 000000000000..fd71e7f4863b
--- /dev/null
+++ b/drivers/otg/otgcore/otg-trace.c
@@ -0,0 +1,881 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg-trace.c
+ * @(#) balden@belcarra.com/seth2.rillanon.org|otg/otgcore/otg-trace.c|20070529052439|20500
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ *
+ */
+/*!
+ * @file otg/otgcore/otg-trace.c
+ * @brief OTG Core Debug Trace Facility
+ *
+ * This implements the OTG Core Trace Facility.
+ *
+ * @ingroup OTGTRACE
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+//#if defined(OTG_LINUX)
+//#include <linux/vmalloc.h>
+//#include <stdarg.h>
+//#endif /* defined(OTG_LINUX) */
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+
+//#if defined(LINUX24)
+//#include <asm/div64.h>
+//#endif
+
+#define STATIC static
+
+#if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) || defined(_OTG_DOXYGEN)
+
+otg_sem_t (otg_trace_sem);
+extern framenum_t otg_hcd_ops_framenum;
+extern framenum_t otg_pcd_ops_framenum;
+extern otg_tick_t (* otg_ocd_ops_ticks) (void);
+extern otg_tick_t (* otg_ocd_ops_elapsed) (otg_tick_t *, otg_tick_t *);
+extern u32 *otg_interrupts;
+
+
+
+#define OTG_TRACE_NAME "trace_otg"
+
+#if 0
+/*!
+ * @typedef struct otg_trace_snapshot
+ */
+typedef struct otg_trace_snapshot {
+ int first;
+ int next;
+ int total;
+ otg_trace_t *traces;
+} otg_trace_snapshot;
+#endif
+
+otg_trace_snapshot otg_snapshot1;
+otg_trace_snapshot otg_snapshot2;
+
+int otg_trace_exiting;
+otg_trace_snapshot *otg_trace_current_traces;
+otg_trace_snapshot *otg_trace_other_traces;
+otg_pthread_mutex_t otg_trace_mutex;
+
+/*! otg_trace_init_snapshot - initialize otg_trace_snapshot struct
+ *
+ * @param tss - pointer to otg_trace_snapshot
+ * @return none
+ */
+
+void otg_trace_init_snapshot(otg_trace_snapshot *tss)
+{
+ tss->first = tss->next = tss->total = 0;
+ memset(tss->traces,0,sizeof(otg_trace_t) * TRACE_MAX);
+}
+
+#if 0
+/*! rel_snapshot - release trace snapshot
+ *
+ * @param tss - pointer to otg_trace_snapshot
+ * @return NULL
+ */
+
+STATIC otg_trace_snapshot *rel_snapshot(otg_trace_snapshot *tss)
+{
+ RETURN_NULL_UNLESS(tss);
+ if (tss->traces) {
+ vfree(tss->traces);
+ tss->traces = NULL;
+ }
+ return NULL;
+}
+
+/*! otg_trace_snapshot - allocate otg trace snapshot memory resource
+ *
+ * @param tss - pointer to otg_trace_snapshot
+ * @return otg_trace_snapshot point on success
+ */
+
+STATIC otg_trace_snapshot *get_snapshot(otg_trace_snapshot *tss)
+{
+ unsigned long flags;
+ UNLESS ((tss->traces = vmalloc(sizeof(otg_trace_t) * TRACE_MAX))) {
+// printk(KERN_ERR "Cannot allocate memory for OTG trace log.\n");
+ return rel_snapshot(tss);
+ }
+ otg_trace_init_snapshot(tss);
+ return tss;
+}
+#endif
+
+#if 0
+/*!
+ * otg_get_trace_info() - get otg trace information
+ * @param p - pointer to trace slot
+ * @return none
+ */
+void otg_get_trace_info(struct otg_instance *otg, otg_trace_t *p)
+{
+ #if defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE)
+ RETURN_UNLESS( (p) );
+
+ //p->hcd_pcd = 0;
+
+ // XXX p->id_gnd = otg_instance_info->current_inputs & ID_GND ? 1 : 0;
+ // XXX if (hcd_instance_private.active) p->hcd_pcd |= 0x1;
+ // XXX if (pcd_instance_private.active) p->hcd_pcd |= 0x2;
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ p->ticks = (otg_ocd_ops_ticks) ? otg_ocd_ops_ticks () : 0;
+ p->interrupts = otg_interrupts ? *otg_interrupts : 0;
+ p->h_framenum = ((otg && otg_hcd_ops_framenum) ? otg_hcd_ops_framenum(otg) : 0);
+ p->p_framenum = ((otg && otg_pcd_ops_framenum) ? otg_pcd_ops_framenum(otg) : 0);
+ if (in_interrupt()) p->flags |= OTG_TRACE_IN_INTERRUPT;
+ //p->in_interrupt = in_interrupt();
+ #endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+}
+#endif
+
+
+/*! otg_trace_get_next - get snapshot info and move to next trace slot
+ *
+ * @param tag - otg_tag type
+ * @param fn - function pointer
+ * @param otg_trace_type otg trace type
+ * @return next trace slot
+ */
+
+STATIC otg_trace_t *otg_trace_get_next(otg_tag_t tag, const char *fn, otg_trace_types_t otg_trace_type)
+{
+ otg_trace_snapshot *tss;
+ otg_trace_t *p;
+ int first, next, last;
+ int gap;
+
+ UNLESS(tag) {
+ printk(KERN_INFO"%s: TAG NULL\n", __FUNCTION__);
+ return NULL;
+ }
+ UNLESS ((tss = otg_trace_current_traces)) {
+ printk(KERN_INFO"%s: TSS NULL\n", __FUNCTION__);
+ return NULL;
+ }
+ UNLESS(tss->traces) {
+ printk(KERN_INFO"%s: TSS->traces NULL\n", __FUNCTION__);
+ return NULL;
+ }
+ UNLESS((p = tss->traces + tss->next)) {
+ printk(KERN_INFO"%s: p NULL\n", __FUNCTION__);
+ return NULL;
+ }
+
+ /* advance */
+ next = tss->next;
+ first = tss->first;
+
+ gap = (first <= next) ? (TRACE_MAX - next) + first : first - next;
+
+ if (1 > gap) first = first + 0x200;
+
+ last = next + 1;
+ first &= TRACE_MASK;
+ last &= TRACE_MASK;
+
+ tss->total += 1;
+ tss->next = last;
+ tss->first = first;
+
+ p->otg_trace_type = otg_trace_type;
+ p->tag = tag->tag;
+ p->function = fn;
+
+ otg_get_trace_info(tag->otg, p);
+
+ return p;
+}
+
+
+/*! otg_trace_dump - setup otg trace with setup information
+ *
+ * @param tag - otg tag
+ * @param fn - function pointe
+ * @param trace_type
+ * @param len
+ * @param dump - pointer to setup information
+ */
+void otg_trace_dump(otg_tag_t tag, const char *fn, otg_trace_types_t trace_type, int len, void *dump)
+{
+ while (len > 0) {
+ otg_trace_t *p;
+ int bytes = MIN(len, sizeof(p->trace.dump));
+
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ if ((p = otg_trace_get_next(tag, fn, trace_type))) {
+ p->va_num_args = bytes;
+ memcpy(&p->trace.dump, dump, bytes);
+ }
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+ len -= bytes;
+ dump += bytes;
+ }
+}
+
+/*! otg_trace_setup - setup otg trace with setup information
+ *
+ * @param tag - otg tag
+ * @param fn - function pointer
+ * @param setup - pointer to setup information
+ */
+void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup)
+{
+ otg_trace_t *p;
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ if ((p = otg_trace_get_next(tag, fn, otg_trace_setup_n))) {
+ p->va_num_args = 0;
+ memcpy(&p->trace.setup, setup, sizeof(p->trace.setup) /*sizeof(struct usbd_device_request)*/);
+ }
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+}
+
+/*! otg_trace_string - put trace message in snapshot
+ *
+ * @param tag - otg tag
+ * @param fn - function pointer
+ * @param fmt - used for formatting trace message
+ * @param str - trace message pointer
+ * @return none
+ */
+void otg_trace_string(otg_tag_t tag, const char *fn, char *fmt, void *str)
+{
+ otg_trace_t *p;
+ int len = MIN(strlen(str), sizeof(p->trace.string));
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ if ((p = otg_trace_get_next(tag, fn, otg_trace_string_n))) {
+ p->va_num_args = len;
+ p->fmt = fmt;
+ memcpy(&p->trace.string, str, len);
+ }
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+}
+
+/*! otg_trace_elapsed - put trace message in snapshot
+ *
+ * @param tag - otg tag
+ * @param fn - function pointer
+ * @param fmt - used for formatting trace message
+ * @param id -
+ * @param ticks
+ * @return none
+ */
+void otg_trace_elapsed(otg_tag_t tag, const char *fn, char *fmt, u32 id, otg_tick_t ticks)
+{
+ otg_trace_t *p;
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ if ((p = otg_trace_get_next(tag, fn, otg_trace_elapsed_n))) {
+ p->va_num_args = 0;
+ p->fmt = fmt;
+ p->trace.ticks = ticks;
+ p->trace.id = id;
+ }
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+}
+
+extern void otg_trace_copy(u32 *p, u32 val);
+
+/*! otg_trace_msg - write trace message to trace slot
+ *
+ * @param tag - otg tag
+ * @param fn - function pointer
+ * @param nargs - argument number
+ * @param fmt - pointer to format string
+ * @param a1, a2, a3, a4, a5, a6, a7, a8
+ * @return none
+ */
+
+void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, u32 a1, u32 a2, u32 a3, u32 a4,
+ u32 a5, u32 a6, u32 a7, u32 a8)
+{
+ //int n;
+ //otg_trace_t trace;
+ //otg_trace_t *p1 = &trace;
+ otg_trace_t *p2 = NULL;
+
+ /* Figure out how many trace slots we're going to need. */
+
+ UNLESS(tag) {
+ //printk(KERN_INFO"%s: TAG NULL\n", __FUNCTION__);
+ return;
+ }
+ UNLESS(nargs <= (OTG_TRACE_MAX_IN_VA)) {
+ printk(KERN_INFO"%s: NARGS to large (%d > %d)\n", __FUNCTION__, nargs, OTG_TRACE_MAX_IN_VA);
+ return;
+ }
+
+ #if 0
+ memset(p1, 0, sizeof(trace));
+
+ // otg_get_trace_info(tag->otg, p1);
+ p->otg_trace_type = otg_trace_type;
+ p->tag = tag->tag;
+ p->function = fn;
+
+ p1->otg_trace_type = otg_trace_msg_va_start_n;
+ p1->tag = tag->tag;
+ p1->function = fn;
+
+ p1->fmt = fmt;
+ p1->va_num_args = nargs;
+
+ p1->trace.val[0] = a1;
+ p1->trace.val[1] = a2;
+ p1->trace.val[2] = a3;
+ p1->trace.val[3] = a4;
+ p1->trace.val[4] = a5;
+ p1->trace.val[5] = a6;
+ p1->trace.val[6] = a7;
+ p1->trace.val[7] = a8;
+ #else
+ #endif
+
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+
+ if((p2 = otg_trace_get_next(tag, fn, otg_trace_msg_va_start_n))) {
+
+ #if 0
+ memcpy(p2, p1, sizeof(otg_trace_t));
+ #else
+ p2->otg_trace_type = otg_trace_msg_va_start_n;
+ p2->tag = tag->tag;
+ p2->function = fn;
+
+ p2->fmt = fmt;
+ p2->va_num_args = nargs;
+
+ p2->trace.val[0] = a1;
+ p2->trace.val[1] = a2;
+ p2->trace.val[2] = a3;
+ p2->trace.val[3] = a4;
+ p2->trace.val[4] = a5;
+ p2->trace.val[5] = a6;
+ p2->trace.val[6] = a7;
+ p2->trace.val[7] = a8;
+ #endif
+ }
+
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+}
+
+/* Proc Filesystem *************************************************************************** */
+
+/*! int slot_in_data - validate slot region
+ *
+ * @param tss - otg trace snapshot pointer
+ * @param slot - slot number to validate
+ * @return 1 for valid, otherwise 0
+ */
+STATIC int slot_in_data(otg_trace_snapshot *tss, int slot)
+{
+ // Return 1 if slot is in the valid data region, 0 otherwise.
+ return (((tss->first < tss->next) && (slot >= tss->first) && (slot < tss->next)) ||
+ ((tss->first > tss->next) && ((slot < tss->next) || (slot >= tss->first))));
+
+}
+
+/*! otg_trace_read_slot - Place a slot pointer in *ps (and possibly *pv), with the older entry (if any) in *po.
+ *
+ * @param tss - pointer to otg trace snapshot
+ * @param index
+ * @param ps - pointer to slot pointer
+ * @param po - pointer to slot pointer
+ * Return: -1 at end of data
+ * 0 if not a valid enty
+ * 1 if entry valid but no older valid entry
+ * 2 if both entry and older are valid.
+ */
+STATIC int otg_trace_read_slot(otg_trace_snapshot *tss, int index, otg_trace_t **ps, otg_trace_t **po)
+{
+ int res;
+ int previous;
+ otg_trace_t *s,*o;
+
+ *ps = *po = NULL;
+ index = (tss->first + index) & TRACE_MASK;
+ // Are we at the end of the data?
+ if (!slot_in_data(tss,index)) {
+ // End of data.
+ return -1;
+ }
+ /* Nope, there's data to show. */
+ s = tss->traces + index;
+ if (!s->tag ||
+ s->otg_trace_type == otg_trace_msg_invalid_n
+ /* || s->otg_trace_type == otg_trace_msg_va_list_n */
+ ) {
+ /* Ignore this slot, it's not valid, or is part
+ of a previously processed varargs. */
+ return 0;
+ }
+ *ps = s;
+ res = 1;
+
+ // Is there a previous event (for "ticks" calculation)?
+ previous = (index - 1) & TRACE_MASK;
+ if (previous != tss->next && tss->total > 1) {
+ // There is a valid previous event.
+ res = 2;
+ o = tss->traces + previous;
+ #if 0
+ /* If the previous event was a varargs event, we want
+ a copy of the first slot, not the last. */
+ while (o->otg_trace_type == otg_trace_msg_va_list_n) {
+ previous = (previous - 1) & TRACE_MASK;
+ if (previous == tss->next) {
+ res = 1;
+ o = NULL;
+ break;
+ }
+ o = tss->traces + previous;
+ }
+ #endif
+ *po = o;
+ }
+
+ return res;
+}
+
+#if defined(CONFIG_OTG_TRACE)
+
+/*! otg_trace_proc_read - implement proc file system read.
+ *
+ * @brief Standard proc file system read function.
+ * @param page -pointer to char to stroe read message
+ * @param count
+ * @param entry position pointer to read
+ * @return read message length
+ */
+int otg_trace_proc_read(char *page, int count, int * pos)
+{
+ int len = 0;
+ int index;
+ int oindex;
+ int rc;
+
+ otg_tick_t ticks = 0;
+
+ unsigned char *cp;
+ int skip = 0;
+ char hcd_pcd;
+ char str[256];
+ int i;
+ struct usbd_device_request *request;
+
+ otg_trace_t *o;
+ otg_trace_t *s;
+ otg_trace_snapshot *tss;
+
+ while (otg_sem_wait(&otg_trace_sem));
+
+ /* Grab the current snapshot, and replace it with the other. This
+ * needs to be atomic WRT otg_trace_msg() calls.
+ */
+ UNLESS (*pos) {
+ // unsigned long flags;
+ tss = otg_trace_other_traces;
+ otg_trace_init_snapshot(tss); // clear previous
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ otg_trace_other_traces = otg_trace_current_traces; // swap snapshots
+ otg_trace_current_traces = tss;
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+ }
+
+ tss = otg_trace_other_traces;
+
+ /* Get the index of the entry to read
+ */
+ s = o = NULL;
+ oindex = index = (*pos)++;
+ do {
+ rc = otg_trace_read_slot(tss,index,&s,&o);
+ switch (rc) {
+ case -1: // End of data.
+ otg_sem_post(&otg_trace_sem);
+ return 0;
+
+ case 0: // Invalid slot, skip it and try the next.
+ index = (*pos)++;
+ break;
+
+ case 1: // s valid, o NULL
+ case 2: // s and o valid
+ break;
+ }
+ } while (rc == 0);
+
+ len = 0;
+
+ UNLESS (oindex)
+ len += sprintf((char *) page + len, "Tag Ints Framenum Ticks\n");
+
+
+ /* If there is a previous trace event, we want to calculate how many
+ * ticks have elapsed siince it happened. Unfortunately, determining
+ * if there _is_ a previous event isn't obvious, since we have to watch
+ * out for startup, varargs and wraparound.
+ */
+ if (o) {
+
+ ticks = otg_tmr_elapsed(&s->ticks, &o->ticks);
+
+ if ((o->interrupts != s->interrupts) || (o->tag != s->tag) ||
+ ((o->flags & OTG_TRACE_IN_INTERRUPT) != (s->flags & OTG_TRACE_IN_INTERRUPT))
+ )
+ skip++;
+ }
+
+ switch (s->flags & OTG_TRACE_HCD_PCD) {
+ default:
+ case 0: hcd_pcd = ' '; break;
+ case 1: hcd_pcd = 'H'; break;
+ case 2: hcd_pcd = 'P'; break;
+ case 3: hcd_pcd = 'B'; break;
+ }
+
+ len += sprintf((char *) page + len, "%s%02x %c%c %6x ", skip?"\n":"",
+ s->tag, (s->flags & OTG_TRACE_ID_GND) ? 'A' : 'B', hcd_pcd, s->interrupts);
+
+
+ //len += sprintf((char *) page + len, "%05x %05x ", s->h_framenum & 0x7fff, s->p_framenum & 0x7fff);
+ len += sprintf((char *) page + len, "%06d ", s->p_framenum & 0x7fff);
+
+
+ if (ticks < 10000)
+ len += sprintf((char *) page + len, "%6duS", (int)ticks);
+ else if (ticks < 10000000) {
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000);
+ len += sprintf((char *) page + len, "%6dmS", (int)ticks / 1000);
+ }
+ else {
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000000);
+ len += sprintf((char *) page + len, "%6dS ", (int)ticks / 1000000);
+ }
+
+ ticks = 0LL;
+
+
+ ticks = otg_tmr_elapsed(&ticks, &s->ticks) / 1000;
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000);
+
+ len += sprintf((char *) page + len, "%6dmS", (int)(ticks & 0xffff));
+
+
+ len += sprintf((char *) page + len, " %s ", s->flags & OTG_TRACE_IN_INTERRUPT ? "--" : "==");
+ len += sprintf((char *) page + len, "%-24s: ",s->function);
+
+
+ switch (s->otg_trace_type) {
+ case otg_trace_msg_invalid_n:
+ len += sprintf((char *) page + len, " -- N/A");
+ break;
+
+ case otg_trace_msg_va_start_n:
+
+ len += sprintf((char *) page + len, s->fmt,
+ s->trace.val[0], s->trace.val[1], s->trace.val[2],
+ s->trace.val[3], s->trace.val[4], s->trace.val[5],
+ s->trace.val[6], s->trace.val[7], s->trace.val[8]
+ );
+
+ break;
+
+ case otg_trace_string_n:
+ i = s->va_num_args;
+ memcpy(str, &s->trace.string, i);
+ str[i] = '\0';
+ len += sprintf((char *) page + len, s->fmt, str);
+ break;
+
+ case otg_trace_elapsed_n:
+ ticks = otg_tmr_elapsed(&s->ticks, &s->trace.ticks);
+
+ i = s->va_num_args;
+ len += sprintf((char *) page + len, "ELAPSED %s ", s->fmt);
+ if (ticks < 10000)
+ len += sprintf((char *) page + len, "%duS", ticks);
+ else {
+ //ticks = otg_do_div(ticks, (otg_tick_t)1000);
+ len += sprintf((char *) page + len, "%dmS", ticks / 1000);
+ }
+ len += sprintf((char *) page + len, " (%x)", s->trace.id);
+ break;
+
+ case otg_trace_recv_n:
+ case otg_trace_send_n:
+ cp = (unsigned char *)&s->trace.dump;
+
+ len += sprintf((char *) page + len, "%s [ ", (s->otg_trace_type == otg_trace_recv_n) ? "RECV" : "SEND" );
+ for (i = 0; i < s->va_num_args; i++) {
+ len += sprintf((char *) page + len, "%02x ", cp[i]);
+ if ((i % 8) == 7)
+ len += sprintf((char *) page + len, " ");
+ }
+ len += sprintf((char *) page + len, "]");
+ break;
+
+ case otg_trace_nrecv_n:
+ case otg_trace_nsend_n:
+ cp = (unsigned char *)&s->trace.dump;
+
+ len += sprintf((char *) page + len, "%s [ ", (s->otg_trace_type == otg_trace_nrecv_n) ? "NRCV" : "NSND" );
+ for (i = 0; i < s->va_num_args; i++) {
+ len += sprintf((char *) page + len, "%02x ", cp[i]);
+
+ switch(i) {
+ case 5:
+ case 11:
+ case 13:
+ case 15:
+ len += sprintf((char *) page + len, "][");
+ break;
+ default:
+ break;
+ }
+ }
+ len += sprintf((char *) page + len, "]");
+ break;
+
+ case otg_trace_setup_n:
+ cp = (unsigned char *)&s->trace.setup;
+ len += sprintf((char *) page + len,
+ "REQUEST [%02x %02x %02x %02x %02x %02x %02x %02x]",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
+ request = (struct usbd_device_request*) &s->trace.setup;
+ len += sprintf((char *) page + len,
+ " bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x",
+ request->bmRequestType,
+ request->bRequest,
+ le16_to_cpu(request->wValue),
+ le16_to_cpu(request->wIndex),
+ le16_to_cpu(request->wLength)
+ );
+
+ break;
+ }
+ len += sprintf((char *) page + len, "\n");
+
+ otg_sem_post(&otg_trace_sem);
+ return len;
+}
+
+#endif
+
+static u32 otg_tags_mask = 0x0;
+extern void otg_trace_exit (void);
+
+/*! otg_trace_obtain_tag(void) - get next unused tag value
+ *
+ * @param data - otg instance pointer
+ * @param msg - message to initialize newly got tag
+ * @return tag valude , 0 for unavailable
+ */
+otg_tag_t otg_trace_obtain_tag(void *data, char *msg)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+ otg_tag_t tag = NULL;
+
+
+ RETURN_NULL_UNLESS((tag = CKMALLOC(sizeof (struct otg_tag))));
+
+ tag->otg = otg;
+ tag->tag = 0;
+ tag->msg = msg;
+ //strncpy(tag->msg, msg, sizeof(tag->msg)-1);
+ //tag->msg[sizeof(tag->msg)-1] = '\0';
+
+ /* Return the next unused tag value [1..32] if one is available,
+ * return 0 if none is available.
+ */
+ while(otg_sem_wait(&otg_trace_sem));
+ if (otg_tags_mask == 0xffffffff || otg_trace_exiting) {
+ // They're all in use.
+ otg_sem_post(&otg_trace_sem);
+ return tag;
+ }
+ if (otg_tags_mask != 0x00000000) {
+ // 2nd or later tag, search for an unused one
+ while (otg_tags_mask & (0x1 << tag->tag)) {
+ tag->tag += 1;
+ }
+ }
+
+ // Successful 1st or later tag.
+ otg_tags_mask |= 0x1 << tag->tag;
+ otg_sem_post(&otg_trace_sem);
+ tag->tag += 1;
+ //printk(KERN_INFO"%s: %s %d\n", __FUNCTION__, msg, tag->tag);
+ return tag;
+}
+
+/*! otg_trace_invalidate_tag(otg_tag_t tag) - invalidate otg trace tag
+ *
+ * @param tag - tag to invalidate
+ * @return 0 on finish
+ */
+otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag)
+{
+ otg_trace_t *p,*e;
+ otg_trace_snapshot *css,*oss;
+
+ //printk(KERN_INFO"%s: %x %s %d\n", __FUNCTION__, tag, tag ? tag->msg : "NULL", tag ? tag->tag : 0);
+
+ RETURN_NULL_UNLESS(tag);
+
+
+ // Nothing to do
+ if ((0 >= tag->tag) || (tag->tag > 32)) {
+ LKFREE(tag);
+ return NULL;
+ }
+
+ // Grab a local copy of the snapshot pointers.
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+ css = otg_trace_current_traces;
+ oss = otg_trace_other_traces;
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+
+
+ // Run through all the trace messages, and invalidate any with the given tag.
+
+ while(otg_sem_wait(&otg_trace_sem));
+ if (css)
+ for (e = TRACE_MAX + (p = css->traces); p < e; p++) {
+ if (p->tag == tag->tag) {
+ p->otg_trace_type = otg_trace_msg_invalid_n;
+ p->tag = 0;
+ }
+ }
+ if (oss)
+ for (e = TRACE_MAX + (p = oss->traces); p < e; p++) {
+ if (p->tag == tag->tag) {
+ p->otg_trace_type = otg_trace_msg_invalid_n;
+ p->tag = 0;
+ }
+ }
+
+ // Turn off the tag
+ otg_tags_mask &= ~(0x1 << (tag->tag-1));
+ if (otg_tags_mask == 0x00000000) { /* otg_trace_exit (); */ }
+ otg_sem_post(&otg_trace_sem);
+ LKFREE(tag);
+ return NULL;
+}
+
+
+
+/* Module init ************************************************************** */
+
+#define OT otg_trace_init_test
+otg_tag_t OT;
+
+/*! otg_trace_modinit - initialization for otg trace
+ *
+ * @return non-zero if not successful.
+ */
+int otg_trace_modinit (void)
+{
+ RETURN_EINVAL_IF(otg_sem_init_unlocked("otg_trace", &otg_trace_sem));
+ otg_trace_exiting = 0;
+
+ OT = otg_trace_obtain_tag(NULL, "OTG Trace Init");
+ RETURN_ENOMEM_IF(!OT);
+ TRACE_MSG0(OT,"--");
+ return 0;
+}
+
+/*! otg_trace_exit - remove procfs entry, free trace data space.
+ */
+void otg_trace_modexit (void)
+{
+ struct otg_tag tag;
+
+ // unsigned long flags;
+ otg_trace_snapshot *c,*o;
+
+ otg_trace_invalidate_tag(OT);
+ otg_trace_exiting = 1;
+
+ otg_pthread_mutex_lock(&otg_trace_mutex); /* lock mutex */
+
+ /* Look for any outstanding tags. */
+ tag.otg = NULL;
+ for (tag.tag = 32; otg_tags_mask && tag.tag > 0; tag.tag--) {
+
+
+ if (otg_tags_mask & (0x1 << (tag.tag-1))) {
+ printk(KERN_INFO"%s: ERROR otg_tags_mask: %x %x tag: %d %s\n", __FUNCTION__,
+ otg_tags_mask, 0x1 << (tag.tag-1), tag.tag, "" /*tag.msg*/);
+
+ //otg_trace_invalidate_tag(&tag);
+ }
+ }
+
+ /* traces are cleared here, the os specific function must have saved them */
+ c = otg_trace_current_traces;
+ o = otg_trace_other_traces;
+ otg_trace_current_traces = otg_trace_other_traces = NULL;
+ otg_pthread_mutex_unlock(&otg_trace_mutex); /* lock mutex */
+
+ /* traces must be released in os specific modexit function */
+}
+
+
+OTG_EXPORT_SYMBOL(otg_trace_dump);
+OTG_EXPORT_SYMBOL(otg_trace_setup);
+OTG_EXPORT_SYMBOL(otg_trace_string);
+OTG_EXPORT_SYMBOL(otg_trace_elapsed);
+
+
+#else /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+int otg_trace_init (void) {return 0;}
+void otg_trace_exit (void) {}
+otg_tag_t otg_trace_obtain_tag(void *p,char *msg1) { return NULL; }
+
+void otg_trace_setup(otg_tag_t tag, const char *fn, void *setup) { }
+void otg_trace_msg(otg_tag_t tag, const char *fn, u8 nargs, char *fmt, ...) { }
+otg_tag_t otg_trace_invalidate_tag(otg_tag_t tag) { return NULL; }
+void otg_trace_modexit (void) { }
+int otg_trace_modinit (void) { return 0; }
+
+
+#endif /* defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE) */
+
+OTG_EXPORT_SYMBOL(otg_trace_msg);
+OTG_EXPORT_SYMBOL(otg_trace_obtain_tag);
+OTG_EXPORT_SYMBOL(otg_trace_invalidate_tag);
+
+
+
+// XXX OTG_EXPORT_SYMBOL(otg_tmr_ticks);
+// XXX OTG_EXPORT_SYMBOL(otg_tmr_elapsed);
diff --git a/drivers/otg/otgcore/otg.c b/drivers/otg/otgcore/otg.c
new file mode 100644
index 000000000000..35ba56a1053f
--- /dev/null
+++ b/drivers/otg/otgcore/otg.c
@@ -0,0 +1,1182 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/otg.c - OTG state machine
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/otg.c|20070919232149|63517
+ *
+ * Copyright (c) 2004-2005 Belcarra
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@lbelcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otgcore/otg.c
+ * @brief OTG Core State Machine Event Processing
+ *
+ * The OTG State Machine receives "input" information passed to it
+ * from the other drivers (pcd, tcd, hcd and ocd) that describe what
+ * is happening.
+ *
+ * The State Machine uses the inputs to move from state to state. Each
+ * state defines four things:
+ *
+ * 1. Reset - what inputs to set or reset on entry to the state.
+ *
+ * 2. Outputs - what output functions to call to set or reset.
+ *
+ * 3. Timeout - an optional timeout value to set
+ *
+ * 4. Tests - a series of tests that allow the state machine to move to
+ * new states based on current or new inputs.
+ *
+ * @ingroup OTGCORE
+ */
+
+//#define OTG_TRACE_DISABLE
+#ifndef OTG_REGRESS
+
+//#include <sys/time.h>
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+//#include <otg/otg-task.h>
+#include <otg/otg-tcd.h>
+#include <otg/otg-hcd.h>
+#include <otg/otg-pcd.h>
+#include <otg/otg-ocd.h>
+//#include <otg/otg-linux.h>
+
+/* XXX these need to be replaced so that we can
+ * support multiple devices
+ */
+
+otg_tick_t (* otg_ocd_ops_ticks) (void);
+otg_tick_t (* otg_ocd_ops_elapsed) (otg_tick_t *, otg_tick_t *);
+
+otg_tick_t (* otg_pcd_ops_ticks) (void);
+otg_tick_t (* otg_pcd_ops_elapsed) (otg_tick_t *, otg_tick_t *);
+
+u32 *otg_interrupts;
+
+framenum_t otg_hcd_ops_framenum;
+framenum_t otg_pcd_ops_framenum;
+
+static char otg_message_buf[256];
+
+#if defined (OTG_WINCE)
+#define TRACE_SM_CURRENT(t, m, o) \
+
+#else
+#define TRACE_SM_CURRENT(t, m, o) \
+ TRACE_STRING(t, "SM_CURRENT: %s",\
+ m \
+ ); \
+ TRACE_MSG4(t, "SM_CURRENT: RESET: %08x SET: %08x %s (%s)",\
+ (u32)(~o->current_inputs), \
+ (u32)(o->current_inputs), \
+ o->current_outputs->name, \
+ o->previous_outputs->name \
+ )
+#endif //OTG_WINCE
+#define TRACE_SM_CHANGE(t, m, o) \
+ TRACE_STRING(t, "SM_CHANGE: %s",\
+ m \
+ ); \
+ TRACE_MSG4(t, "SM_CHANGE: RESET: %08x SET: %08x %s (%s)",\
+ (u32)(~o->current_inputs), \
+ (u32)(o->current_inputs), \
+ o->current_outputs->name, \
+ o->previous_outputs->name \
+ )
+
+#define TRACE_SM_NEW(t, m, o) \
+ TRACE_STRING(t, "SM_NEW: %s",\
+ m \
+ ); \
+ TRACE_MSG4(t, "SM_NEW: RESET: %08x SET: %08x %s (%s)",\
+ (u32)(~o->current_inputs), \
+ (u32)(o->current_inputs), \
+ o->current_outputs->name, \
+ o->previous_outputs->name \
+ )
+
+
+#define TRACE_SM_INPUTS(t, m, u, c) \
+ TRACE_STRING(t, "SM_INPUTS: %s",\
+ m \
+ ); \
+ TRACE_MSG3(t, "SM_INPUTS: RESET: %08x SET: %08x CUR: %08x",\
+ (u32)(u >> 32), \
+ (u32)(u & 0xffffffff), \
+ c \
+ )
+
+#endif
+
+//DECLARE_MUTEX (otg_sem);
+
+/*!
+ * Output change lookup table - this maps the current output and the desired
+ * output to what should be done. This is just so that we don't redo changes,
+ * if the output does not change we do not want to re-call the output function
+ */
+u8 otg_output_lookup[4][4] = {
+ /* NC SET RESET PULSE, <--- current/ new */
+ { NC, NC, NC, PULSE, }, /* NC */
+ { SET, NC, SET, PULSE, }, /* SET */
+ { RESET, RESET, NC, PULSE, }, /* RESET */
+ { PULSE, PULSE, PULSE, PULSE, }, /* UNKNOWN */
+};
+
+
+/*!
+ * otg_new() - check OTG new inputs and select new state to be in
+ *
+ * This is used by the OTG event handler to see if the state should
+ * change based on the current input values.
+ *
+ * @param otg pointer to the otg instance.
+ * @return either the next OTG state or invalid_state if no change.
+ */
+static int otg_new(struct otg_instance *otg)
+{
+ int state = otg->state;
+ struct otg_test *otg_test = otg_firmware_loaded ? otg_firmware_loaded->otg_tests : NULL;
+ /* get a copy of current inputs */
+ otg_current_t input_mask = ((otg_current_t)otg->current_inputs) | ((otg_current_t)(~otg->current_inputs) << 32);
+
+ UNLESS(otg_test) return invalid_state;
+
+ TRACE_MSG3(CORE, "OTG_NEW: %s inputs: %08x %08x", otg_get_state_name(state),
+ (u32)(input_mask>>32&0xffffffff), (u32)(input_mask&0xffffffff));
+
+ /* iterate across the otg inputs table, for each entry that matches the current
+ * state, test the input_mask to see if a state transition is required.
+ */
+ for (; otg_test->target != invalid_state; otg_test++) {
+
+ otg_current_t test1 = otg_test->test1;
+ otg_current_t test2 = otg_test->test2;
+ otg_current_t test3 = otg_test->test3;
+
+ CONTINUE_UNLESS(otg_test->state == state); // skip states that don't match
+
+
+ TRACE_MSG7(CORE, "OTG_NEW: %s (%s, %d) test1: %08x %08x test2 %08x %08x",
+ otg_get_state_name(otg_test->state),
+ otg_get_state_name(otg_test->target),
+ otg_test->test,
+ (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff),
+ (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff)
+ );
+
+ /* the otg inputs table has multiple masks that define between multiple tests. Each
+ * test is a simple OR to check if specific inputs are present.
+ */
+ CONTINUE_UNLESS (
+ (!otg_test->test1 || ((test1 = otg_test->test1 & input_mask))) &&
+ (!otg_test->test2 || ((test2 = otg_test->test2 & input_mask))) &&
+ (!otg_test->test3 || ((test3 = otg_test->test3 & input_mask))) /*&&
+ (!otg_test->test4 || (otg_test->test4 & input_mask)) */ );
+
+ TRACE_MSG7(CORE, "OTG_NEW: GOTO %s test1: %08x %08x test2 %08x %08x test3 %08x %08x",
+ otg_get_state_name(otg_test->target),
+ (u32)(test1>>32&0xffffffff), (u32)(test1&0xffffffff),
+ (u32)(test2>>32&0xffffffff), (u32)(test2&0xffffffff),
+ (u32)(test3>>32&0xffff), (u32)(test3&0xffff)
+ );
+
+ return otg_test->target;
+ }
+ TRACE_MSG0(CORE, "OTG_NEW: finis");
+
+ return invalid_state;
+}
+
+
+/*!
+ * otg_write_state_message_irq() -
+ *
+ * Send a message to the otg management application.
+ * @param otg -otg instance pointer
+ * @param msg
+ * @param reset
+ * @param inputs
+ */
+void otg_write_state_message_irq(struct otg_instance *otg, char *msg, otg_current_t reset, u32 inputs)
+{
+ sprintf(otg_message_buf,
+ "State: %s reset: %08x set: %08x inputs: %08x ", msg, (u32)(reset>>32), (u32)(reset&0xffffffff), inputs);
+ otg_message_buf[95] = '\0';
+ otg_message(otg, otg_message_buf);
+}
+
+/*!
+ * otg_write_output_message_irq() -
+ *
+ * Send a message to the otg management application.
+ * @param otg - otg instance pointer
+ * @param msg
+ * @param val
+ */
+void otg_write_output_message_irq(struct otg_instance *otg, char *msg, int val)
+{
+ strcpy(otg_message_buf, "Output: ");
+ strncat(otg_message_buf, msg, 64 - strlen(otg_message_buf));
+ if (val == 2) strncat(otg_message_buf, "/", 64 - strlen(otg_message_buf));
+ otg_message_buf[sizeof(otg_message_buf)] = '\0';
+ otg_message(otg, otg_message_buf);
+}
+
+/*!
+ * otg_write_timer_message_irq() -
+ *
+ * Send a message to the otg management application.
+ * @param otg - otg instance pointer
+ * @param msg
+ * @param val
+ */
+void otg_write_timer_message_irq(struct otg_instance *otg, char *msg, int val)
+{
+ if (val < 10000)
+ sprintf(otg_message_buf, "Timer: %s %d uS %u", msg, val, (u32)otg_tmr_ticks());
+ else if (val < 10000000)
+ //sprintf(otg_message_buf, " %d mS\n", ticks / 1000);
+ //sprintf(otg_message_buf, "Timer: %s %d mS %u", msg, val >> 10, otg_tmr_ticks());
+ sprintf(otg_message_buf, "Timer: %s %d mS %u", msg, val / 1000, (u32)otg_tmr_ticks());
+ else
+ //sprintf(otg_message_buf, " %d S\n", ticks / 1000000);
+ //sprintf(otg_message_buf, "Timer: %s %d S %u", msg, val >> 20, otg_tmr_ticks());
+ sprintf(otg_message_buf, "Timer: %s %d S %u", msg, val / 1000000, (u32)otg_tmr_ticks());
+
+ otg_message_buf[sizeof(otg_message_buf)] = '\0';
+ otg_message(otg, otg_message_buf);
+}
+
+//#if defined(LINUX24)
+/*!
+ * otg_write_info_message() -
+ *
+ * Send a message to the otg management application.
+ * @param privdata - pcd_instance type pointer
+ * @param msg -
+ */
+void otg_write_info_message(void *privdata, char *msg)
+{
+ struct pcd_instance *pcd_instance = (struct pcd_instance *) privdata;
+
+ struct otg_instance *otg = pcd_instance ? pcd_instance->otg : NULL;
+
+ RETURN_UNLESS(otg);
+ sprintf(otg_message_buf, "Info: %s", msg);
+ otg_message_buf[sizeof(otg_message_buf)] = '\0';
+ otg_message(otg, otg_message_buf); // XXX
+}
+
+#if 0
+/*!
+ * otg_write_reset_message_irq() -
+ *
+ * Send a message to the otg management application.
+ * @param otg - otg instance pointer
+ * @param msg
+ * @param val
+ */
+void otg_write_reset_message(struct otg_instance *otg, char *msg, int val)
+{
+ sprintf(otg_message_buf, "State: %s reset: %x", msg, val);
+ otg_message_buf[sizeof(otg_message_buf)] = '\0';
+ otg_message(otg, otg_message_buf);
+}
+#endif
+
+//#if defined(LINUX24)
+OTG_EXPORT_SYMBOL(otg_write_info_message);
+//#endif
+
+char otg_change_names[4] = {
+ '_', ' ', '/', '#',
+};
+
+/*!
+ * otg_process_event() - process OTG events and determine OTG outputs to reflect new state
+ *
+ * This function is passed a mask with changed input values. This
+ * is passed to the otg_new() function to see if the state should
+ * change. If it changes then the output functions required for
+ * the new state are called.
+ *
+ * @param otg pointer to the otg instance.
+ * @param inputs input mask
+ * @param tag trace tag to use for tracing
+ * @param msg message for tracing
+ */
+static void otg_process_event(struct otg_instance *otg, otg_current_t inputs, otg_tag_t tag, char *msg)
+{
+ u32 current_inputs = otg->current_inputs;
+ int current_state = otg->state;
+ u32 inputs_set = (u32)(inputs & 0xffffffff); // get changed inputs that SET something
+ u32 inputs_reset = (u32) (inputs >> 32); // get changed inputs that RESET something
+ int target;
+
+ RETURN_UNLESS(otg && inputs);
+
+ TRACE_SM_INPUTS(tag, msg, inputs, current_inputs);
+ TRACE_MSG2(CORE, "otg->active: %d %s", otg->active, msg);
+
+ /* Special Overrides - These are special tests that satisfy specific injunctions from the
+ * OTG Specification.
+ */
+ if (inputs_set & inputs_reset) { // verify that there is no overlap between SET and RESET masks
+ TRACE_MSG1(CORE, "OTG_EVENT: ERROR attempting to set and reset the same input: %08x", inputs_set & inputs_reset);
+ return;
+ }
+ /* don't allow bus_req and bus_drop to be set at the same time
+ */
+ if ((inputs_set & (bus_req | bus_drop)) == (bus_req | bus_drop) ) {
+ TRACE_MSG2(CORE, "OTG_EVENT: ERROR attempting to set both bus_req and bus_drop: %08x %08x",
+ inputs_set, (bus_req | bus_drop));
+ return;
+ }
+ /* set bus_drop_ if bus_req, set bus_req_ if bus_drop
+ */
+ if (inputs_set & bus_req) {
+ inputs |= bus_drop_;
+ TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_drop/: %08x", inputs_set);
+ }
+ if (inputs_set & bus_drop) {
+ inputs |= bus_req_;
+ TRACE_MSG1(CORE, "OTG_EVENT: forcing bus_req/: %08x", inputs_set);
+ }
+
+ otg->current_inputs &= ~inputs_reset;
+ otg->current_inputs |= inputs_set;
+ //TRACE_MSG3(CORE, "OTG_EVENT: reset: %08x set: %08x inputs: %08x", inputs_reset, inputs_set, otg->current_inputs);
+
+ TRACE_SM_CHANGE(tag, msg, otg);
+
+ RETURN_IF(otg->active);
+
+ otg->active++;
+
+ /* Search the state input change table to see if we need to change to a new state.
+ * This may take several iterations.
+ */
+ while (invalid_state != (target = otg_new(otg))) {
+
+ struct otg_state *otg_state;
+ //int original_state = otg->state;
+ otg_current_t current_outputs;
+ otg_current_t output_results;
+ otg_current_t new_outputs;
+ int i;
+
+ /* if previous output started timer, then cancel timer before proceeding
+ */
+ if ((otg_state = otg->current_outputs) && otg_state->tmout && otg->start_timer) {
+ TRACE_MSG0(CORE, "reseting timer");
+ otg->start_timer(otg, 0);
+ }
+
+ BREAK_UNLESS(otg_firmware_loaded);
+
+ BREAK_UNLESS(target < otg_firmware_loaded->number_of_states);
+
+ otg_state = otg_firmware_loaded->otg_states + target;
+
+ BREAK_UNLESS(otg_state->name);
+
+ otg->previous = otg->state;
+ otg->state = target;
+ otg->tickcount = otg_tmr_ticks();
+
+
+ otg->previous_outputs= otg->current_outputs;
+ //otg->current_outputs = NULL;
+
+
+ /* A matching input table rule has been found, we are transitioning to a new
+ * state. We need to find the new state entry in the output table.
+ * XXX this could be a table lookup instead of linear search.
+ */
+
+ current_outputs = otg->outputs;
+ new_outputs = otg_state->outputs;
+ output_results = 0;
+
+ TRACE_SM_NEW(tag, msg, otg);
+
+ /* reset any inputs that the new state want's reset
+ */
+ otg_process_event(otg, otg_state->reset | TMOUT_, tag, otg_state->name);
+
+ otg_write_state_message_irq(otg, otg_state->name, otg_state->reset, otg->current_inputs);
+
+#if 1
+ switch (target) { /* C.f. 6.6.1.12 b_conn reset */
+ case otg_disabled:
+ otg->current_inputs = 0;
+ otg->outputs = 0;
+ break;
+ default:
+ //otg_event(otg, not(b_conn), "a_host/ & a_suspend/: set b_conn/");
+ break;
+ }
+#endif
+ #if 0
+ TRACE_MSG5(CORE, "OTG_EVENT: OUTPUT MAX:%d OUTPUTS CURRENT: %08x %08x NEW: %08x %08x",
+ MAX_OUTPUTS,
+ (u32)(current_outputs >> 32),
+ (u32)(current_outputs & 0xffffffff),
+ (u32)(new_outputs >> 32),
+ (u32)(new_outputs & 0xffffffff)
+ );
+ #endif
+
+ /* Iterate across the outputs bitmask, calling the appropriate functions
+ * to make required changes. The current outputs are saved and we DO NOT
+ * make calls to change output values until they change again.
+ */
+ for (i = 0; i < MAX_OUTPUTS; i++) {
+
+ u8 current_output = (u8)(current_outputs & 0x3);
+ u8 new_output = (u8)(new_outputs & 0x3);
+ u8 changed = otg_output_lookup[new_output][current_output];
+
+ output_results >>= 2;
+
+ switch (changed) {
+ case NC:
+ output_results |= ((current_outputs & 0x3) << (MAX_OUTPUTS * 2));
+ break;
+ case SET:
+ case RESET:
+ output_results |= (((otg_current_t)changed) << (MAX_OUTPUTS * 2));
+ case PULSE:
+
+
+ otg_write_output_message_irq(otg, otg_output_names[i], changed);
+
+ //TRACE_MSG3(CORE, "OTG_EVENT: CHECKING OUTPUT %s %d %s",
+ // otg_state->name, i, otg_output_names[i]);
+
+ BREAK_UNLESS(otg->otg_output_ops[i]); // Check if we have an output routine
+
+ TRACE_MSG7(CORE, "OTG_EVENT: CALLING OUTPUT %s %d %s%c cur: %02x new: %02x chng: %02x",
+ otg_state->name, i, otg_output_names[i],
+ otg_change_names[changed&0x3],
+ current_output, new_output,
+ changed);
+
+ otg->otg_output_ops[i](otg, changed); // Output changed, call function to do it
+
+ //TRACE_MSG2(CORE, "OTG_EVENT: OUTPUT FINISED %s %s", otg_state->name, otg_output_names[i]);
+ break;
+ }
+
+ current_outputs >>= 2;
+ new_outputs >>= 2;
+ }
+
+ output_results >>= 2;
+ otg->outputs = output_results;
+ otg->current_outputs = otg_state;
+
+ //TRACE_MSG2(CORE, "OTG_EVENT: STATE: otg->current_outputs: %08x %08x",
+ // (u32)(otg->outputs & 0xffffffff), (u32)(otg->outputs >> 32));
+
+ if (((otg->outputs & pcd_en_out_set) == pcd_en_out_set) &&
+ ((otg->outputs & hcd_en_out_set) == hcd_en_out_set))
+ {
+ TRACE_MSG0(CORE, "WARNING PCD_EN and HCD_EN both set");
+ }
+
+ /* start timer?
+ */
+ CONTINUE_UNLESS(otg_state->tmout);
+ //TRACE_MSG1(CORE, "setting timer: %d", otg_state->tmout);
+ if (otg->start_timer) {
+ otg_write_timer_message_irq(otg, otg_state->name, otg_state->tmout);
+ otg->start_timer(otg, otg_state->tmout);
+ }
+ else {
+ //TRACE_MSG0(CORE, "NO TIMER");
+ otg_process_event(otg, TMOUT_, CORE, otg_state->name);
+ }
+
+ }
+ TRACE_MSG0(CORE, "finishing");
+ otg->active = 0;
+
+ if (current_state == otg->state)
+ TRACE_MSG0(CORE, "OTG_EVENT: NOT CHANGED");
+
+}
+
+#if 0
+#if defined(LINUX24)
+/*!
+ * otg_write_input_message_irq() -
+
+ * @param inputs input mask
+ * @param tag trace tag to use for tracing
+ * @param msg message for tracing
+ */
+void otg_write_input_message_irq(struct otg_instance *otg, otg_current_t inputs, otg_tag_t tag, char *msg)
+{
+ u32 reset = (u32)(inputs >> 32);
+ u32 set = (u32) (inputs & 0xffffffff);
+
+ char buf[64];
+ //snprintf(buf, sizeof(buf), "Input: %08x %08x %s", *inputs >> 32, *inputs & 0xffffffff, msg ? msg : "NULL");
+ //TRACE_MSG3(tag, "Inputs: %08x %08x %s", set, reset, msg ? msg : "NULL");
+ #ifdef OTG_WINCE
+ sprintf(buf, "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL");
+ #else /* OTG_WINCE */
+ snprintf(buf, sizeof(buf), "Inputs: %08x %08x %s", reset, set, msg ? msg : "NULL");
+ #endif /* OTG_WINCE */
+ buf[63] = '\0';
+ otg_message(otg, buf);
+}
+#endif
+#endif
+
+/*
+ */
+static otg_current_t otg_events[64];
+static otg_tag_t otg_tags[64];
+static char * otg_msgs[64];
+static u8 otg_head, otg_tail;
+
+
+/*!
+ * otg_do_work() - Bottom half handler to process sent or received urbs.
+ *
+ * @param otg - otg instance pointer
+ * @param inputs - input events mask
+ * @param tag - otg tag
+ * @param msg - message
+ * @param queued - BOOL flag for queuing event handler
+ */
+void otg_do_work(struct otg_instance *otg, otg_current_t inputs, otg_tag_t tag, char *msg, BOOL queued )
+{
+ u32 reset = (u32)(inputs >> 32);
+ u32 set = (u32) (inputs & 0xffffffff);
+
+ TRACE_SM_INPUTS(tag, msg ? msg : "NULL", inputs, otg->current_inputs);
+ otg_message_buf[0] = '\0';
+ snprintf(otg_message_buf, sizeof(otg_message_buf),
+ "Inputs: %08x %08x %s [%s]", reset, set, msg ? msg : "NULL", queued ? "Q":"D");
+ otg_message_buf[sizeof(otg_message_buf)] = '\0';
+
+ otg_message(otg, otg_message_buf);
+ otg_process_event(otg, inputs, tag, msg);
+}
+
+/*!
+ * otg_dequeue_tasklet() - tasklet to dequeue events
+ *
+ * @param data pointer to parameters struct
+ */
+void *otg_dequeue_tasklet(otg_tasklet_arg_t data)
+{
+ struct otg_instance *otg = (struct otg_instance *) data;
+ TRACE_MSG0(CORE, "DEQUEUE - START");
+ /* lock interrupts */
+ while (otg_head != otg_tail) {
+
+
+ /* get input information
+ */
+ otg_current_t inputs = otg_events[otg_head];
+ otg_tag_t tag = otg_tags[otg_head];
+ char * msg = otg_msgs[otg_head];
+
+ TRACE_MSG0(CORE, "DEQUEUE");
+ otg_head = (otg_head + 1) & 0x3f;
+ otg_do_work(otg, inputs, tag, msg, TRUE);
+ }
+ TRACE_MSG0(CORE, "DEQUEUE - FINISH");
+ return NULL;
+}
+
+/*!
+ * otg_queue_event() - otg input change event handler
+ * @param otg pointer to the otg instance.
+ * @param inputs input mask
+ * @param tag trace tag to use for tracing
+ * @param msg message for tracing
+ * @return non-zero if state changed.
+ */
+void otg_queue_event(struct otg_instance *otg, otg_current_t inputs, otg_tag_t tag, char *msg)
+{
+ //unsigned long flags;
+ u8 otg_tail_save;
+
+ u32 reset = (u32)(inputs >> 32);
+ u32 set = (u32) (inputs & 0xffffffff);
+ TRACE_MSG3(CORE, "QUEUE: RESET: %08x SET: %08x %s", reset, set, msg);
+
+ /* atomic insertion */
+ otg_pthread_mutex_lock(&otg->mutex); /* lock mutex */
+ otg_tail_save = otg_tail;
+ otg_events[otg_tail] = inputs;
+ otg_tags[otg_tail] = tag;
+ otg_msgs[otg_tail] = msg;
+
+ otg_tail = (otg_tail + 1) & 0x3f;
+
+ /* check for overrun */
+ if (otg_tail == otg_head)
+ otg_tail = otg_tail_save;
+
+ otg_pthread_mutex_unlock(&otg->mutex); /* unlock mutex */
+
+ if(otg->tasklet) otg_tasklet_start(otg->tasklet);
+ //TRACE_MSG0(CORE, "FINISHED");
+}
+
+/*!
+ * otg_event() - otg input change event handler
+ * @param otg pointer to the otg instance.
+ * @param inputs input mask
+ * @param tag trace tag to use for tracing
+ * @param msg message for tracing
+ * @return non-zero if state changed.
+ */
+void otg_event(struct otg_instance *otg, otg_current_t inputs, otg_tag_t tag, char *msg)
+{
+ #if 1
+ otg_queue_event(otg, inputs, tag, msg);
+ #else
+ #ifdef LINUX26
+ if (in_atomic()) {
+ printk(KERN_INFO"%s: IN_ATOMIC %s\n", __FUNCTION__, msg);
+ return;
+ }
+ if (in_interrupt()) {
+ printk(KERN_INFO"%s: IN_INTERRUPT %s\n", __FUNCTION__, msg);
+ return;
+ }
+ #endif /* LINUX26 */
+
+ UNLESS (/*in_atomic() ||*/ down_trylock(&otg->event)) {
+ printk(KERN_INFO"%s: DIRECT %s\n", __FUNCTION__, msg);
+ TRACE_MSG0(CORE, "DIRECT");
+ otg_do_work(otg, inputs, tag, msg, FALSE);
+ //UP(&otg->event);
+ while (otg_sem_wait(&otg->event));
+ //TRACE_MSG0(CORE, "FINISHED");
+ }
+ else
+ otg_queue_event(otg, inputs, tag, msg);
+ #endif
+
+}
+
+
+/*!
+ * otg_event_set_irq() -set changed otg event for handling
+ * @param otg pointer to the otg instance.
+ * @param changed
+ * @param flag
+ * @param input input mask
+ * @param tag trace tag to use for tracing
+ * @param msg message for tracing
+ * @return non-zero if state changed.
+ */
+void otg_event_set_irq(struct otg_instance *otg, int changed, int flag, u32 input, otg_tag_t tag, char *msg)
+{
+ RETURN_UNLESS(changed);
+ TRACE_MSG4(tag, "%s: %08x changed: %d flag: %d", msg, input, changed, flag);
+ otg_queue_event(otg, flag ? input : _NOT(input), tag, msg);
+}
+
+
+/*!
+ * otg_serial_number() - set the device serial number
+ *
+ * @param otg pointer to the otg instance.
+ * @param serial_number_str
+ */
+void otg_serial_number (struct otg_instance *otg, char *serial_number_str)
+{
+ int i;
+ char *cp = serial_number_str;
+
+ //DOWN(&otg_sem);
+ //while (otg_sem_wait(&otg_sem));
+
+ for (i = 0; cp && *cp && (i < OTGADMIN_MAXSTR); cp++) {
+ CONTINUE_UNLESS (isxdigit(*cp));
+ otg->serial_number[i++] = toupper(*cp);
+ }
+ otg->serial_number[i] = '\0';
+
+ //TRACE_MSG2(CORE, "serial_number_str: %s serial_number: %s", serial_number_str, otg->serial_number);
+
+ //UP(&otg_sem);
+ //while(otg_sem_wait(&otg_sem));
+}
+
+
+/*!
+ * otg_init() - create and initialize otg instance
+ *
+ * Called to initialize the OTG state machine. This will cause the state
+ * to change from the invalid_state to otg_disabled.
+ *
+ * If CONFIG_OTG_TR_AUTO is defined thten
+ * an initial enable_otg event is generated. This will cause
+ * the state to move from otg_disabled to otg_enabled. The
+ * requird drivers will be initialized by calling the appropriate
+ * output functions:
+ *
+ * - pcd_init_func
+ * - hcd_init_func
+ * - tcd_init_func
+ *
+ * @param otg pointer to the otg instance.
+ */
+void otg_init (struct otg_instance *otg)
+{
+
+ //TRACE_MSG0(CORE, "START");
+
+ //init_MUTEX(&otg->command);
+ //otg_sem_init("otg_command", &otg->command, 0, 0);
+ //DOWN(&otg_sem);
+ //otg_sem_post(&otg_sem);
+ otg->outputs = tcd_init_out_ | pcd_init_out_ | hcd_init_out_;
+
+ RETURN_UNLESS((otg->tasklet = otg_tasklet_init("otg_event", otg_dequeue_tasklet, (otg_tasklet_arg_t) otg, CORE)));
+ //otg->tasklet->debug = TRUE;
+
+ /* This will move the state machine into the otg_disabled state
+ */
+ otg_event(otg, enable_otg, CORE, "enable_otg"); // XXX
+
+ #if defined(CONFIG_OTG_TR_AUTO)
+ otg_event(otg, AUTO, CORE, "AUTO"); // XXX
+ #endif /* defined(CONFIG_OTG_TR_AUTO) */
+}
+
+
+/*!
+ * otg_exit()
+ *
+ * This is called by the driver that started the state machine to
+ * cause it to exit. The state will move to otg_disabled.
+ *
+ * The appropriate output functions will be called to disable the
+ * peripheral drivers.
+ *
+ * @param otg pointer to the otg instance.
+ */
+void otg_exit (struct otg_instance *otg)
+{
+ TRACE_MSG0(CORE, "OTG_EXIT");
+
+ //DOWN(&otg_sem);
+ //while(otg_sem_wait(&otg_sem));
+
+ //TRACE_MSG0(CORE, "OTG_EXIT");
+ //otg_event(otg, exit_all | not(a_bus_req) | a_bus_drop | not(b_bus_req), "tcd_otg_exit");
+
+ //printk(KERN_INFO"%s: otg->state: %d START\n", __FUNCTION__, otg->state);
+ otg_event(otg, enable_otg_, CORE, "enable_otg_");
+
+ while (otg->state != otg_disabled) {
+ //printk(KERN_INFO"%s: otg->state: %d SLEEP\n", __FUNCTION__, otg->state);
+ //SCHEDULE_TIMEOUT(10 * HZ);
+ otg_sleep(1);
+ }
+ //printk(KERN_INFO"%s: otg->state: %d FINISH\n", __FUNCTION__, otg->state);
+
+ otg_tasklet_exit(otg->tasklet);
+ otg->tasklet = NULL;
+
+ //TRACE_MSG0(CORE, "UP OTG_SEM");
+ //UP(&otg_sem);
+ //otg_sem_post(&otg_sem);
+
+ // XXX MODULE UNLOCK HERE
+}
+
+
+OTG_EXPORT_SYMBOL(otg_queue_event);
+OTG_EXPORT_SYMBOL(otg_event);
+OTG_EXPORT_SYMBOL(otg_serial_number);
+OTG_EXPORT_SYMBOL(otg_init);
+OTG_EXPORT_SYMBOL(otg_exit);
+OTG_EXPORT_SYMBOL(otg_event_set_irq);
+
+/* ********************************************************************************************* */
+
+/*!
+ * otg_gen_init_func() -
+ *
+ * This is a default tcd_init_func. It will be used
+ * if no other is provided.
+ *
+ * @param otg pointer to the otg instance.
+ * @param flag set or reset
+ */
+void otg_gen_init_func (struct otg_instance *otg, u8 flag)
+{
+ TRACE_MSG1(CORE, "GENERIC INIT %s", flag ? "SET" : "RESET");
+ otg_event(otg, OCD_OK, CORE, "GENERIC OK");
+}
+
+
+/*! otg_gen_start_timer -
+ * Fake - Set or reset timer to interrupt in number of uS (micro-seconds).
+ * This is only suitable for MN or TR firmware.
+ *
+ * @param otg otg instance pointer
+ * @param usec
+ * @return 0
+ */
+int otg_gen_start_timer(struct otg_instance *otg, int usec)
+{
+ otg_event(otg, TMOUT, CORE, "FAKE TMOUT");
+ return 0;
+}
+
+
+/*!
+ * otg_hcd_set_ops() -
+ * @param hcd_ops - host operations table to use
+ * @param otg pointer to otg instance
+ * @return pointer to host controller driver instance with initialized operations
+ */
+struct hcd_instance * otg_set_hcd_ops(struct otg_instance *otg, struct hcd_ops *hcd_ops)
+{
+ struct hcd_instance *hcd = NULL;
+ otg->hcd_ops = hcd_ops;
+ if (hcd_ops) {
+ RETURN_NULL_UNLESS((hcd = CKMALLOC(sizeof(struct hcd_instance))));
+ hcd->otg = otg;
+ hcd->TAG = otg_trace_obtain_tag(otg, "set_hcd_ops");
+ otg->hcd = hcd;
+ otg->otg_output_ops[HCD_INIT_OUT] = hcd_ops->hcd_init_func;
+ otg->otg_output_ops[HCD_EN_OUT] = hcd_ops->hcd_en_func;
+ otg->otg_output_ops[HCD_RH_OUT] = hcd_ops->hcd_rh_func;
+ otg->otg_output_ops[LOC_SOF_OUT] = hcd_ops->loc_sof_func;
+ otg->otg_output_ops[LOC_SUSPEND_OUT] = hcd_ops->loc_suspend_func;
+ otg->otg_output_ops[REMOTE_WAKEUP_EN_OUT] = hcd_ops->remote_wakeup_en_func;
+ otg->otg_output_ops[HNP_EN_OUT] = hcd_ops->hnp_en_func;
+ otg_hcd_ops_framenum = hcd_ops->framenum;
+ }
+ else {
+ if (otg->hcd) {
+ otg->hcd->TAG = otg_trace_invalidate_tag(otg->hcd->TAG);
+ LKFREE(otg->hcd);
+ otg->hcd = NULL;
+ }
+ otg->otg_output_ops[HCD_INIT_OUT] =
+ otg->otg_output_ops[HCD_EN_OUT] =
+ otg->otg_output_ops[HCD_RH_OUT] =
+ otg->otg_output_ops[LOC_SOF_OUT] =
+ otg->otg_output_ops[LOC_SUSPEND_OUT] =
+ otg->otg_output_ops[REMOTE_WAKEUP_EN_OUT] =
+ otg->otg_output_ops[HNP_EN_OUT] = NULL;
+ otg_hcd_ops_framenum = NULL;
+ }
+
+ UNLESS (otg->otg_output_ops[HCD_INIT_OUT]) {
+ TRACE_MSG0(CORE, "USING OTG_GEN_INIT_FUNC");
+ otg->otg_output_ops[HCD_INIT_OUT] = otg_gen_init_func;
+ }
+
+ return hcd;
+}
+
+/*!
+ * otg_ocd_set_ops() -Connect ocd operations to otg instance
+ *
+ * @param otg - otg instance pointer
+ * @param ocd_ops - ocd operations table to use
+ * @return pointer to ocd instance
+ */
+struct ocd_instance * otg_set_ocd_ops(struct otg_instance *otg, struct ocd_ops *ocd_ops)
+{
+ struct ocd_instance *ocd = NULL;
+ otg->ocd_ops = ocd_ops;
+ if (ocd_ops) {
+ RETURN_NULL_UNLESS((ocd = CKMALLOC(sizeof(struct ocd_instance))));
+ ocd->otg = otg;
+ ocd->TAG = otg_trace_obtain_tag(otg, "set_ocd_ops");
+ otg->ocd = ocd;
+ otg->otg_output_ops[OCD_INIT_OUT] = ocd_ops->ocd_init_func;
+ otg->start_timer = ocd_ops->start_timer;
+ otg_ocd_ops_ticks = ocd_ops->ticks;
+ otg_ocd_ops_elapsed = ocd_ops->elapsed;
+ otg_interrupts = &otg->interrupts;
+ }
+ else {
+ if (otg->ocd) {
+ otg->ocd->TAG = otg_trace_invalidate_tag(otg->ocd->TAG);
+ LKFREE(otg->ocd);
+ otg->ocd = NULL;
+ }
+ otg_ocd_ops_ticks = NULL;
+ otg_ocd_ops_elapsed = NULL;
+ otg->otg_output_ops[OCD_INIT_OUT] = NULL;
+ otg->start_timer = NULL;
+ otg_interrupts = NULL;
+ }
+
+ UNLESS (otg->otg_output_ops[OCD_INIT_OUT]) {
+ TRACE_MSG0(CORE, "USING OTG_GEN_INIT_FUNC");
+ otg->otg_output_ops[OCD_INIT_OUT] = otg_gen_init_func;
+ }
+
+ UNLESS (otg->start_timer)
+ otg->start_timer = otg_gen_start_timer;
+
+ return ocd;
+}
+
+/*!
+ * otg_pcd_set_ops() - connect pcd operations to otg instance operations
+ * @param otg - otg instance pointer
+ * @param pcd_ops - pcd operations table to use
+ * @return pointer to initialized pcd instance
+ */
+struct pcd_instance * otg_set_pcd_ops(struct otg_instance *otg, struct pcd_ops *pcd_ops)
+{
+ struct pcd_instance *pcd = NULL;
+ otg->pcd_ops = pcd_ops;
+ if (pcd_ops) {
+ RETURN_NULL_UNLESS((pcd = CKMALLOC(sizeof(struct pcd_instance))));
+ pcd->otg = otg;
+ pcd->TAG = otg_trace_obtain_tag(otg, "set_pcd_ops");
+ otg->pcd = pcd;
+ otg->otg_output_ops[PCD_INIT_OUT] = pcd_ops->pcd_init_func;
+ otg->otg_output_ops[PCD_EN_OUT] = pcd_ops->pcd_en_func;
+ otg->otg_output_ops[REMOTE_WAKEUP_OUT] = pcd_ops->remote_wakeup_func;
+ otg_pcd_ops_framenum = pcd_ops->framenum;
+
+ if (pcd_ops->tcd_en_func)
+ otg->otg_output_ops[TCD_EN_OUT] = pcd_ops->tcd_en_func;
+ if (pcd_ops->dp_pullup_func)
+ otg->otg_output_ops[DP_PULLUP_OUT] = pcd_ops->dp_pullup_func;
+ otg_pcd_ops_ticks = pcd_ops->ticks;
+ otg_pcd_ops_elapsed = pcd_ops->elapsed;
+ }
+ else {
+ if (otg->pcd) {
+ otg->pcd->TAG = otg_trace_invalidate_tag(otg->pcd->TAG);
+ LKFREE(otg->pcd);
+ otg->pcd = NULL;
+ }
+ otg_pcd_ops_ticks = NULL;
+ otg_pcd_ops_elapsed = NULL;
+ otg->otg_output_ops[PCD_INIT_OUT] =
+ otg->otg_output_ops[PCD_EN_OUT] =
+ otg->otg_output_ops[REMOTE_WAKEUP_OUT] = NULL;
+ otg_pcd_ops_framenum = NULL;
+ }
+
+ UNLESS (otg->otg_output_ops[PCD_INIT_OUT]) {
+ TRACE_MSG0(CORE, "USING OTG_GEN_INIT_FUNC");
+ otg->otg_output_ops[PCD_INIT_OUT] = otg_gen_init_func;
+ }
+
+ return pcd;
+}
+
+/*!
+ * otg_tcd_set_ops() - connect tcd opereations to otg instance operations
+ * @param otg - otg instance pointer
+ * @param tcd_ops - tcd operations table to use
+ * @return pointer to tcd intance
+ */
+struct tcd_instance * otg_set_tcd_ops(struct otg_instance *otg, struct tcd_ops *tcd_ops)
+{
+ struct tcd_instance *tcd = NULL;
+ otg->tcd_ops = tcd_ops;
+ if (tcd_ops) {
+ RETURN_NULL_UNLESS((tcd = CKMALLOC(sizeof(struct tcd_instance))));
+ tcd->otg = otg;
+ tcd->TAG = otg_trace_obtain_tag(otg, "set_tcd_ops");
+ otg->tcd = tcd;
+ otg->otg_output_ops[TCD_INIT_OUT] = tcd_ops->tcd_init_func;
+ otg->otg_output_ops[TCD_EN_OUT] = tcd_ops->tcd_en_func;
+ otg->otg_output_ops[CHRG_VBUS_OUT] = tcd_ops->chrg_vbus_func;
+ otg->otg_output_ops[DRV_VBUS_OUT] = tcd_ops->drv_vbus_func;
+ otg->otg_output_ops[DISCHRG_VBUS_OUT] = tcd_ops->dischrg_vbus_func;
+ otg->otg_output_ops[DP_PULLUP_OUT] = tcd_ops->dp_pullup_func;
+ otg->otg_output_ops[DM_PULLUP_OUT] = tcd_ops->dm_pullup_func;
+ otg->otg_output_ops[DP_PULLDOWN_OUT] = tcd_ops->dp_pulldown_func;
+ otg->otg_output_ops[DM_PULLDOWN_OUT] = tcd_ops->dm_pulldown_func;
+ //otg->otg_output_ops[PERIPHERAL_HOST] = tcd_ops->peripheral_host_func;
+ otg->otg_output_ops[CLR_OVERCURRENT_OUT] = tcd_ops->overcurrent_func;
+ otg->otg_output_ops[DM_DET_OUT] = tcd_ops->dm_det_func;
+ otg->otg_output_ops[DP_DET_OUT] = tcd_ops->dp_det_func;
+ otg->otg_output_ops[CR_DET_OUT] = tcd_ops->cr_det_func;
+ otg->otg_output_ops[AUDIO_OUT] = tcd_ops->audio_func;
+ otg->otg_output_ops[CHARGE_PUMP_OUT] = tcd_ops->charge_pump_func;
+ otg->otg_output_ops[BDIS_ACON_OUT] = tcd_ops->bdis_acon_func;
+ //otg->otg_output_ops[MX21_VBUS_DRAIN] = tcd_ops->mx21_vbus_drain_func;
+ otg->otg_output_ops[ID_PULLDOWN_OUT] = tcd_ops->id_pulldown_func;
+ otg->otg_output_ops[UART_OUT] = tcd_ops->uart_func;
+ otg->otg_output_ops[MONO_OUT] = tcd_ops->mono_func;
+ }
+ else {
+ if (otg->tcd){
+ otg->tcd->TAG = otg_trace_invalidate_tag(otg->tcd->TAG);
+ LKFREE(otg->tcd);
+ otg->tcd = NULL;
+ }
+ otg->otg_output_ops[TCD_INIT_OUT] =
+ otg->otg_output_ops[TCD_EN_OUT] =
+ otg->otg_output_ops[CHRG_VBUS_OUT] =
+ otg->otg_output_ops[DRV_VBUS_OUT] =
+ otg->otg_output_ops[DISCHRG_VBUS_OUT] =
+ otg->otg_output_ops[DP_PULLUP_OUT] =
+ otg->otg_output_ops[DM_PULLUP_OUT] =
+ otg->otg_output_ops[DP_PULLDOWN_OUT] =
+ otg->otg_output_ops[DM_PULLDOWN_OUT] =
+ //otg->otg_output_ops[PERIPHERAL_HOST] =
+ otg->otg_output_ops[CLR_OVERCURRENT_OUT] =
+ otg->otg_output_ops[DM_DET_OUT] =
+ otg->otg_output_ops[DP_DET_OUT] =
+ otg->otg_output_ops[CR_DET_OUT] =
+ otg->otg_output_ops[AUDIO_OUT] =
+ otg->otg_output_ops[CHARGE_PUMP_OUT] =
+ otg->otg_output_ops[BDIS_ACON_OUT] =
+ //otg->otg_output_ops[MX21_VBUS_DRAIN] =
+ otg->otg_output_ops[ID_PULLDOWN_OUT] =
+ otg->otg_output_ops[UART_OUT] =
+ otg->otg_output_ops[MONO_OUT] = NULL;
+ }
+
+ UNLESS (otg->otg_output_ops[TCD_INIT_OUT]) {
+ TRACE_MSG0(CORE, "USING OTG_GEN_INIT_FUNC");
+ otg->otg_output_ops[TCD_INIT_OUT] = otg_gen_init_func;
+ }
+
+ return tcd;
+}
+
+/*!
+ * otg_get_ocd_info
+ */
+void otg_get_ocd_info(struct otg_instance *otg, otg_tick_t *ticks, u16 *p_framenum)
+{
+ *ticks = (otg_ocd_ops_ticks) ? otg_ocd_ops_ticks () : ((otg_pcd_ops_ticks) ? otg_pcd_ops_ticks () : 0);
+ if (p_framenum)
+ *p_framenum = ((otg && otg_pcd_ops_framenum) ? otg_pcd_ops_framenum(otg) : 0);
+}
+
+
+#if defined(CONFIG_OTG_TRACE)
+/*!
+ * otg_get_trace_info() - get otg trace inforamtion
+ * @param p - pointer to tarce slot
+ * @return none
+ */
+void otg_get_trace_info(struct otg_instance *otg, otg_trace_t *p)
+{
+ #if !defined(OTG_TRACE_DISABLE) && ( defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE))
+ RETURN_UNLESS( (p) );
+
+ //p->hcd_pcd = 0;
+
+ // XXX p->id_gnd = otg_instance_info->current_inputs & ID_GND ? 1 : 0;
+ // XXX if (hcd_instance_private.active) p->hcd_pcd |= 0x1;
+ // XXX if (pcd_instance_private.active) p->hcd_pcd |= 0x2;
+
+ #if 1
+ p->ticks = (otg && otg_ocd_ops_ticks) ? otg_ocd_ops_ticks () : ((otg && otg_pcd_ops_ticks) ? otg_pcd_ops_ticks () : 0);
+ p->interrupts = (otg && otg_interrupts) ? *otg_interrupts : 0;
+ p->h_framenum = ((otg && otg_hcd_ops_framenum) ? otg_hcd_ops_framenum(otg) : 0);
+ p->p_framenum = ((otg && otg_pcd_ops_framenum) ? otg_pcd_ops_framenum(otg) : 0);
+ #else
+ otg_get_ocd_info(otg, &p->ticks, &p->interrupts, &p->h_framenum, &p->p_framenum);
+ #endif
+
+ #ifdef LINUX26
+ if (in_interrupt()) p->flags |= OTG_TRACE_IN_INTERRUPT;
+ #endif /* LINUX26 */
+ //p->in_interrupt = in_interrupt();
+ #endif /* !defined(OTG_TRACE_DISABLE) && ( defined(CONFIG_OTG_TRACE) || defined(CONFIG_OTG_TRACE_MODULE)) */
+}
+
+void otg_trace_copy(u32 *p, u32 val)
+{
+ //*p = val;
+}
+
+#endif
+
+
+/*!
+ * otg_tmr_ticks() - get ticks
+ *
+ * @return number of ticks.
+ */
+otg_tick_t otg_tmr_ticks(void)
+{
+ struct timeval tv;
+
+ if (otg_ocd_ops_ticks)
+ return otg_ocd_ops_ticks ();
+
+ if (otg_pcd_ops_ticks)
+ return otg_pcd_ops_ticks ();
+
+ otg_gettimeofday(&tv);
+ return tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+/*!
+ * otg_tmr_elapsed() -
+ * @param t1
+ * @param t2
+ * @return number of uSecs between t1 and t2 ticks.
+ */
+otg_tick_t otg_tmr_elapsed(otg_tick_t *t1, otg_tick_t *t2)
+{
+ return otg_ocd_ops_elapsed ? otg_ocd_ops_elapsed (t1, t2) :
+ (otg_pcd_ops_elapsed ? otg_pcd_ops_elapsed (t1, t2) :
+ (((*t1 > *t2) ? (*t1 - *t2) : (*t2 - *t1))));
+}
+
+OTG_EXPORT_SYMBOL(otg_set_hcd_ops);
+OTG_EXPORT_SYMBOL(otg_set_ocd_ops);
+OTG_EXPORT_SYMBOL(otg_set_pcd_ops);
+OTG_EXPORT_SYMBOL(otg_set_tcd_ops);
+OTG_EXPORT_SYMBOL(otg_set_usbd_ops);
+OTG_EXPORT_SYMBOL(otg_tmr_ticks);
+OTG_EXPORT_SYMBOL(otg_tmr_elapsed);
+OTG_EXPORT_SYMBOL(otg_get_ocd_info);
+#if defined(LINUX24)
+//OTG_EXPORT_SYMBOL(otg_write_input_message_irq);
+#endif
+
+
+#if defined(OTG_WINCE)
+#else /* defined(OTG_WINCE) */
+#endif /* defined(OTG_WINCE) */
diff --git a/drivers/otg/otgcore/usbp-bops.c b/drivers/otg/otgcore/usbp-bops.c
new file mode 100644
index 000000000000..eb230827c289
--- /dev/null
+++ b/drivers/otg/otgcore/usbp-bops.c
@@ -0,0 +1,2042 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/usbp-bops.c - USB Device Prototype
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/usbp-bops.c|20070816060612|46629
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otgcore/usbp-bops.c
+ * @brief Bus Interface related functions.
+ *
+ * This implements the functions used to implement Peripheral Controller Drivers (PCD.)
+ *
+ * @ingroup USBDCORE
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+//#include <otg/otg-task.h>
+#include <otg/otg-trace.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-pcd.h>
+
+int usbd_procfs_init (struct usbd_bus_instance *);
+void usbd_procfs_exit (struct usbd_bus_instance *);
+
+
+struct usbd_simple_driver *usbp_find_simple_driver(char *);
+struct usbd_simple_instance *usbp_alloc_simple_instance(struct usbd_simple_driver *, struct usbd_bus_instance *);
+void usbp_dealloc_simple_instance(struct usbd_simple_instance *simple_instance);
+
+void usbp_alloc_simple_configuration_descriptor(struct usbd_simple_instance *simple_instance, int cfg_size, int hs);
+
+struct usbd_composite_driver *usbp_find_composite_driver(char *);
+struct usbd_composite_instance *usbp_alloc_composite_instance(struct usbd_composite_driver *, struct usbd_bus_instance *);
+
+void usbp_dealloc_composite_instance(struct usbd_composite_instance *composite_instance);
+
+void usbp_alloc_composite_configuration_descriptor(struct usbd_composite_instance *composite_instance, int cfg_size, int hs);
+
+void *usbd_device_reset(void *data);
+void *usbd_device_suspended(void *data);
+void *usbd_device_resumed(void *data);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Note that all of the list functions overlapped operation using the usbd_bus_sem;
+ *
+ * usbd_device_bh
+ *
+ * usbd_register_bus
+ * usbd_deregister_bus
+ * usbd_enable_function
+ * usbd_disable_function
+ */
+//DECLARE_MUTEX(usbd_bus_sem);
+otg_sem_t usbd_bus_sem;
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Bus Interface Management:
+ * usbd_register_bus()
+ * usbd_deregister_bus()
+ */
+
+/*! urb_link_init() - initialize urb linke
+ *
+ * Initialize an urb_link to be a single element list.
+ * If the urb_link is being used as a distinguished list head
+ * the list is empty when the head is the only link in the list.
+ *
+ * @param ul link to urb
+ */
+static INLINE void urb_link_init (urb_link * ul)
+{
+ ul->prev = ul->next = ul;
+}
+
+struct usbd_endpoint_map ep0_endpoint_map_array[2];
+
+#if defined(OTG_C99)
+struct usbd_interface_instance ep0_instance = {
+ .function = {
+ .name = "EP0",
+ .function_type = function_ep0, },
+ .endpoint_map_array = ep0_endpoint_map_array,
+};
+#else /* defined(OTG_C99) */
+struct usbd_interface_instance ep0_instance;
+#endif /* defined(OTG_C99) */
+
+
+/*!
+ * usbd_register_bus() - called by a USB BUS INTERFACE driver to register a bus driver
+ *
+ * Used by a USB Bus interface driver to register itself with the usb device layer.
+ *
+ * @param driver
+ * @param ep0_wMaxPacketSize
+ * @return non-zero if error
+ */
+struct usbd_bus_instance *usbd_register_bus (struct usbd_bus_driver *driver, int ep0_wMaxPacketSize)
+{
+ int i;
+ struct usbd_bus_instance *bus = NULL;
+
+ RETURN_NULL_IF(otg_sem_wait(&usbd_bus_sem));
+
+ THROW_UNLESS((bus = CKMALLOC (sizeof (struct usbd_bus_instance))), error);
+ THROW_UNLESS((bus->reset_bh = otg_workitem_init("busreset", usbd_device_reset, bus, USBD)), error);
+ THROW_UNLESS((bus->resume_bh = otg_workitem_init("busact", usbd_device_resumed, bus, USBD)), error);
+ THROW_UNLESS((bus->suspend_bh = otg_workitem_init("busidle", usbd_device_suspended, bus, USBD)), error);
+
+
+ //bus->reset_bh->debug = TRUE;
+ //bus->resume_bh->debug = TRUE;
+ //bus->suspend_bh->debug = TRUE;
+
+ bus->driver = driver;
+ bus->endpoints = bus->driver->max_endpoints;
+ bus->bmAttributes = bus->driver->bmAttributes;
+ bus->bMaxPower = bus->driver->bMaxPower;
+ TRACE_MSG2(USBD, "bmAttributes: %02x bMaxPower: %02x", bus->bmAttributes, bus->bMaxPower);
+
+
+ THROW_IF(!(bus->endpoint_array = CKMALLOC(sizeof (struct usbd_endpoint_instance) * bus->endpoints)), error);
+
+ for (i = 0; i < bus->endpoints; i++) {
+ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i;
+ //endpoint->physical_endpoint = i;
+ urb_link_init (&endpoint->rdy);
+ endpoint->bus = bus;
+ //init_MUTEX(&endpoint->sem);
+ }
+
+ bus->ep0 = &ep0_instance;
+ bus->ep0->function.bus = bus;
+
+ #if !defined(OTG_C99)
+ bus->ep0->function.name = "EP0";
+ bus->ep0->function.function_type = function_ep0;
+ bus->ep0->endpoint_map_array = ep0_endpoint_map_array;
+ #endif /* defined(OTG_C99) */
+
+ for (i = 0; i < 2; i++) {
+ bus->ep0->endpoint_map_array[i].wMaxPacketSize[0] =
+ bus->ep0->endpoint_map_array[i].wMaxPacketSize[1] = ep0_wMaxPacketSize;
+ bus->ep0->endpoint_map_array[i].endpoint = bus->endpoint_array;
+ }
+
+ bus->device_state = STATE_CREATED;
+ bus->status = USBD_OPENING;
+ TRACE_MSG1(USBD, "bus->status: %d", bus->status);
+
+ //new_usbd_bus_instance = bus;
+
+ #if defined(CONFIG_OTG_TRACE)
+ THROW_IF(usbd_procfs_init (bus), error);
+ #endif /* defined(CONFIG_OTG_TRACE) */
+
+ CATCH(error) {
+ if (bus) {
+ if (bus->reset_bh) otg_workitem_exit(bus->reset_bh);
+ if (bus->resume_bh) otg_workitem_exit(bus->resume_bh);
+ if (bus->suspend_bh) otg_workitem_exit(bus->suspend_bh);
+ LKFREE(bus);
+ }
+ bus->endpoints = 0;
+ bus = NULL;
+ }
+ otg_sem_post(&usbd_bus_sem);
+ return bus;
+}
+
+/*!
+ * usbd_deregister_bus() - called by a USB BUS INTERFACE driver to deregister a bus driver
+ *
+ * Used by a USB Bus interface driver to de-register itself with the usb device
+ * layer.
+ *
+ * @param bus
+ */
+void usbd_deregister_bus (struct usbd_bus_instance *bus)
+{
+ while (otg_sem_wait(&usbd_bus_sem));
+
+ #if defined(CONFIG_OTG_TRACE)
+ usbd_procfs_exit (bus);
+ #endif /* defined(CONFIG_OTG_TRACE) */
+ //new_usbd_bus_instance = NULL;
+
+ if (bus->reset_bh) otg_workitem_exit(bus->reset_bh);
+ if (bus->resume_bh) otg_workitem_exit(bus->resume_bh);
+ if (bus->suspend_bh) otg_workitem_exit(bus->suspend_bh);
+ LKFREE (bus->arg);
+ LKFREE (bus->endpoint_array);
+ LKFREE (bus);
+ //bus->endpoints = 0;
+ otg_sem_post(&usbd_bus_sem);
+}
+
+OTG_EXPORT_SYMBOL(usbd_register_bus);
+OTG_EXPORT_SYMBOL(usbd_deregister_bus);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * bus_function_enable() - enable a usbd function driver for use
+ *
+ * This will copy descriptors from the function driver and get them ready
+ * for use.
+ * @param bus
+ * @param function
+ * @param serial_number
+ * @return non-zero if error
+ */
+int
+bus_function_enable (struct usbd_bus_instance *bus, struct usbd_function_instance *function, char *serial_number)
+{
+ struct usbd_simple_instance *simple_instance = NULL;
+ struct usbd_simple_driver *simple_driver = NULL;
+ struct usbd_composite_instance *composite_instance = NULL;
+ struct usbd_composite_driver *composite_driver = NULL;
+
+ struct usbd_device_description *device_description = NULL;
+ //struct usbd_device_descriptor *device_descriptor = NULL;
+#ifdef CONFIG_OTG_HIGH_SPEED
+ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ //struct usbd_otg_descriptor *otg_descriptor;
+ int i;
+ int rc = 0;
+
+ TRACE_MSG1(USBD, "function: %x", function);
+
+ /* point functions at the bus
+ */
+ function->bus = bus;
+
+ if (function_simple == function->function_type) {
+ simple_instance = (struct usbd_simple_instance *)function;
+ simple_driver = (struct usbd_simple_driver *)simple_instance->function.function_driver;
+ simple_driver->driver.flags |= FUNCTION_ENABLED;
+ /* call enable function
+ */
+ if (simple_driver->driver.fops->function_enable)
+ simple_driver->driver.fops->function_enable (function);
+
+ /* update device descriptor
+ */
+ RETURN_ZERO_IF(!(device_description = simple_driver->device_description));
+ }
+ if (function_composite == function->function_type) {
+
+ composite_instance = (struct usbd_composite_instance *)function;
+ composite_driver = (struct usbd_composite_driver *)composite_instance->function.function_driver;
+
+ TRACE_MSG0(USBD, "enable composite");
+
+ /* call enable functions
+ */
+ if (composite_driver->driver.fops->function_enable)
+ composite_driver->driver.fops->function_enable (function);
+ composite_driver->driver.flags |= FUNCTION_ENABLED;
+
+ TRACE_MSG0(USBD, "enable class");
+ if (composite_instance->class_instance) {
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ struct usbd_class_driver *class_driver =
+ (struct usbd_class_driver *)class_instance->function.function_driver;
+
+ composite_instance->class_instance->function.bus = bus;
+ if (class_driver->driver.fops->function_enable)
+ class_driver->driver.fops->function_enable
+ ((struct usbd_function_instance *)class_instance);
+ class_driver->driver.flags |= FUNCTION_ENABLED;
+ }
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interfaces_array = composite_instance->interfaces_array;
+ struct usbd_interface_instance *interface_instance = interfaces_array + i;
+ struct usbd_interface_driver *interface_driver;
+ CONTINUE_UNLESS(interface_instance);
+ TRACE_MSG1(USBD, "enable interface[%d]", i);
+ interface_driver = (struct usbd_interface_driver *)interface_instance->function.function_driver;
+ interface_instance->function.bus = bus;
+ if (interface_driver->driver.fops->function_enable)
+ interface_driver->driver.fops->function_enable
+ ((struct usbd_function_instance *)interface_instance);
+ interface_driver->driver.flags |= FUNCTION_ENABLED;
+ }
+
+ /* update device descriptor
+ */
+ RETURN_ZERO_IF(!(device_description = composite_driver->device_description));
+ }
+
+
+ //RETURN_ZERO_IF(!(device_descriptor = device_description->device_descriptor));
+
+ device_description->bMaxPacketSize0 = bus->driver->maxpacketsize;
+ #if 0
+ if (strlen (serial_number))
+ device_descriptor->iSerialNumber = usbd_alloc_string (serial_number);
+ else
+ device_descriptor->iSerialNumber = usbd_alloc_string (device_description->iSerialNumber);
+ #endif
+
+ #if 0
+ // XXX not used
+ if (function_simple == function->function_type) {
+
+ if ((otg_descriptor = device_description->otg_descriptor)) {
+ otg_descriptor->bmAttributes = usbd_otg_bmattributes(function);
+ simple_driver->otg_descriptor = otg_descriptor;
+ }
+
+ //simple_instance->device_descriptor = device_descriptor;
+
+ }
+ #endif
+ #if 0
+ // XXX not used
+ if (function_composite == function->function_type) {
+ if ((otg_descriptor = device_description->otg_descriptor)) {
+ otg_descriptor->bmAttributes = usbd_otg_bmattributes(function);
+ composite_driver->otg_descriptor = otg_descriptor;
+ }
+
+ }
+ #endif
+ return rc;
+}
+
+/*!
+ * bus_function_disable() - disable a usbd function driver for use
+ *
+ * This will copy descriptors from the function driver and get them ready
+ * for use.
+ * @param bus
+ * @param function
+ * @return non-zero if error
+ */
+int
+bus_function_disable (struct usbd_bus_instance *bus, struct usbd_function_instance *function)
+{
+ struct usbd_simple_instance *simple_instance = NULL;
+ struct usbd_simple_driver *simple_driver = NULL;
+ struct usbd_composite_instance *composite_instance = NULL;
+ struct usbd_composite_driver *composite_driver = NULL;
+
+ struct usbd_device_description *device_description = NULL;
+ //struct usbd_device_descriptor *device_descriptor = NULL;
+#ifdef CONFIG_OTG_HIGH_SPEED
+ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor;
+#endif /* CONFIG_OTG_HIGH_SPEED */
+ //struct usbd_otg_descriptor *otg_descriptor;
+ int i;
+ int rc = 0;
+
+ TRACE_MSG1(USBD, "function: %x", function);
+
+ /* point functions at the bus
+ */
+ function->bus = bus;
+
+ if (function_simple == function->function_type) {
+ simple_instance = (struct usbd_simple_instance *)function;
+ simple_driver = (struct usbd_simple_driver *)simple_instance->function.function_driver;
+ simple_driver->driver.flags &= ~FUNCTION_ENABLED;
+ /* call disable function
+ */
+ if (simple_driver->driver.fops->function_disable)
+ simple_driver->driver.fops->function_disable (function);
+
+ /* update device descriptor
+ */
+ RETURN_ZERO_IF(!(device_description = simple_driver->device_description));
+ }
+ if (function_composite == function->function_type) {
+
+ composite_instance = (struct usbd_composite_instance *)function;
+ composite_driver = (struct usbd_composite_driver *)composite_instance->function.function_driver;
+
+ TRACE_MSG0(USBD, "disable composite");
+
+ /* call disable functions
+ */
+ if (composite_driver->driver.fops->function_disable)
+ composite_driver->driver.fops->function_disable (function);
+ composite_driver->driver.flags &= ~FUNCTION_ENABLED;
+
+ TRACE_MSG0(USBD, "disable class");
+ if (composite_instance->class_instance) {
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ struct usbd_class_driver *class_driver =
+ (struct usbd_class_driver *)class_instance->function.function_driver;
+
+ composite_instance->class_instance->function.bus = bus;
+ if (class_driver->driver.fops->function_disable)
+ class_driver->driver.fops->function_disable
+ ((struct usbd_function_instance *)class_instance);
+ class_driver->driver.flags |= FUNCTION_ENABLED;
+ }
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interfaces_array = composite_instance->interfaces_array;
+ struct usbd_interface_instance *interface_instance = interfaces_array + i;
+ struct usbd_interface_driver *interface_driver;
+ CONTINUE_UNLESS(interface_instance);
+ TRACE_MSG1(USBD, "disable interface[%d]", i);
+ interface_driver = (struct usbd_interface_driver *)interface_instance->function.function_driver;
+ interface_instance->function.bus = bus;
+ if (interface_driver->driver.fops->function_disable)
+ interface_driver->driver.fops->function_disable
+ ((struct usbd_function_instance *)interface_instance);
+ interface_driver->driver.flags |= FUNCTION_ENABLED;
+ }
+ }
+ return rc;
+}
+
+/*!
+ * Function Management:
+ * usbd_enable_function()
+ * usbd_disable_function()
+ *
+ */
+
+/*!
+ * usbd_enable_function() - called to enable the desired function
+ *
+ * Used by a USB Bus interface driver to create a virtual device.
+ *
+ * @param bus
+ * @param arg
+ * @param serial_number
+ * @return non-zero if error
+ */
+int usbd_enable_function(struct usbd_bus_instance *bus, char *arg, char *serial_number)
+{
+ struct usbd_composite_driver *composite_driver = NULL;
+ struct usbd_composite_instance *composite_instance = NULL;
+
+ struct usbd_simple_driver *simple_driver = NULL;
+ struct usbd_simple_instance *simple_instance = NULL;
+
+ //struct usbd_configuration_descriptor *configuration_descriptor = NULL;
+ struct usbd_endpoint_map *endpoint_map_array = NULL;
+ struct usbd_endpoint_request *requestedEndpoints = NULL;
+
+ //struct otg_list_node *lhd = NULL;
+ int len = 0;
+ int i;
+ int rc = -EINVAL;
+ int epn;
+ int endpointsRequested = 0;
+ //int endpoints;
+
+ TRACE_STRING(USBD, "serial_number: %s", serial_number);
+
+ RETURN_EINVAL_IF(otg_sem_wait(&usbd_bus_sem));
+
+ if (arg && bus->arg)
+ LKFREE(bus->arg);
+
+ if (arg) {
+ bus->arg = LSTRDUP(arg);
+ len = strlen(arg);
+ }
+
+ for (i = 1; i < bus->endpoints; i++) {
+ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + i;
+ endpoint->active_urb = NULL;
+ }
+
+ TRACE_STRING(USBD, "arg: %s", arg);
+
+ //PREPARE_WORK_ITEM(bus->reset_bh, usbd_device_reset, bus);
+ //PREPARE_WORK_ITEM(bus->suspend_bh, usbd_device_suspended, bus);
+ //PREPARE_WORK_ITEM(bus->resume_bh, usbd_device_resumed, bus);
+ bus->status = USBD_OK;
+ TRACE_MSG1(USBD, "bus->status: %d", bus->status);
+
+ /* find requested function
+ */
+ if ((simple_driver = usbp_find_simple_driver(arg))) {
+
+ TRACE_MSG0(USBD, "simple");
+
+ // allocate simple_instance
+ //
+ THROW_UNLESS((simple_instance = usbp_alloc_simple_instance (simple_driver, bus)), error);
+ bus->function_instance = (struct usbd_function_instance *)simple_instance;
+
+ #if 0
+ simple_driver->iManufacturer =
+ usbd_realloc_string (&simple_instance->function, strindex_product,
+ simple_driver->device_description->iManufacturer);
+ #else
+ simple_driver->iManufacturer = usbd_alloc_string (&simple_instance->function,
+ simple_driver->device_description->iManufacturer);
+ #endif
+ simple_driver->iProduct = usbd_alloc_string (&simple_instance->function,
+ simple_driver->device_description->iProduct);
+
+ if (serial_number && strlen (serial_number))
+ simple_driver->iSerialNumber = usbd_alloc_string (&simple_instance->function, serial_number);
+ else
+ simple_driver->iSerialNumber = usbd_alloc_string (&simple_instance->function,
+ simple_driver->device_description->iSerialNumber);
+
+ bus_function_enable (bus, &simple_instance->function, serial_number);
+
+ requestedEndpoints = simple_driver->requestedEndpoints;
+ endpointsRequested = simple_driver->endpointsRequested;
+ endpoint_map_array = simple_instance->endpoint_map_array;
+ }
+
+ else if ((composite_driver = usbp_find_composite_driver(arg))) {
+
+ TRACE_MSG0(USBD, "composite");
+ THROW_UNLESS((composite_instance = usbp_alloc_composite_instance (composite_driver, bus)), error);
+
+ TRACE_MSG1(USBD, "composite: composite_instance: %x", composite_instance);
+
+ bus->function_instance = (struct usbd_function_instance *)composite_instance;
+
+ /* CLASS MISC devices must have SubClass set to 0x2 and Protocol set to 0x1
+ */
+ if ((USB_CLASS_MISC == composite_driver->device_description->bDeviceClass) ) {
+ composite_driver->device_description->bDeviceSubClass = 0x2;
+ composite_driver->device_description->bDeviceProtocol = 0x1;
+ }
+
+ #if 0
+ composite_driver->iManufacturer =
+ usbd_realloc_string (&composite_instance->function, strindex_product,
+ composite_driver->device_description->iManufacturer);
+ #else
+ composite_driver->iManufacturer = usbd_alloc_string (&composite_instance->function,
+ composite_driver->device_description->iManufacturer);
+ #endif
+ composite_driver->iProduct = usbd_alloc_string (&composite_instance->function,
+ composite_driver->device_description->iProduct);
+ if (serial_number && strlen (serial_number))
+ composite_driver->iSerialNumber = usbd_alloc_string (&composite_instance->function, serial_number);
+ else
+ composite_driver->iSerialNumber = usbd_alloc_string (&composite_instance->function,
+ composite_driver->device_description->iSerialNumber);
+
+ //TRACE_MSG3(USBD, "iManufacturer: %d iProduct: %d iSerialNumber: %d", composite_driver->iManufacturer,
+ // composite_driver->iProduct, composite_driver->iSerialNumber);
+
+ TRACE_MSG0(USBD, "composite - enable");
+ bus_function_enable (bus, &composite_instance->function, serial_number);
+
+ requestedEndpoints = composite_instance->requestedEndpoints;
+ endpointsRequested = composite_instance->endpointsRequested;
+ endpoint_map_array = composite_instance->endpoint_map_array;
+ }
+ else
+ THROW(error);
+
+
+ /* Request endpoints from bus interface driver
+ */
+ THROW_IF(bus->driver->bops->request_endpoints( bus, endpoint_map_array, endpointsRequested, requestedEndpoints), error);
+
+ /* create configuration descriptors
+ */
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ for (i = 0; i < 2; i++)
+ #else /* CONFIG_OTG_HIGH_SPEED */
+ for (i = 0; i < 1; i++)
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+ {
+ if (simple_instance)
+ usbp_alloc_simple_configuration_descriptor (simple_instance,
+ simple_instance->configuration_size, i);
+ else if (composite_instance)
+ usbp_alloc_composite_configuration_descriptor (composite_instance,
+ composite_instance->configuration_size, i);
+ }
+
+ /* set the endpoints in the peripheral driver
+ */
+ THROW_IF(bus->driver->bops->set_endpoints( bus, endpointsRequested, endpoint_map_array), error);
+
+ /* iterate across the logical endpoint map to copy appropriate information
+ * into the physical endpoint instance array
+ */
+ for (epn = 0; epn < endpointsRequested; epn++) {
+
+ struct usbd_endpoint_map *endpoint_map = endpoint_map_array + epn;
+ int physicalEndpoint = endpoint_map->physicalEndpoint[0];
+ struct usbd_endpoint_instance *endpoint = bus->endpoint_array + physicalEndpoint;
+ int hs;
+
+ endpoint_map->endpoint = endpoint;
+
+ for (hs = 0; hs < 2; hs++) {
+
+ endpoint->physicalEndpoint[hs] = endpoint_map->physicalEndpoint[0];
+ endpoint->bEndpointAddress[hs] = endpoint_map->bEndpointAddress[hs];
+ endpoint->bmAttributes[hs] = endpoint_map->bmAttributes[hs];
+ endpoint->transferSize[hs] = endpoint_map->transferSize[hs];
+ endpoint->wMaxPacketSize[hs] = endpoint_map->wMaxPacketSize[hs];
+
+ switch(endpoint->bEndpointAddress[hs] & USB_ENDPOINT_DIR_MASK) {
+ case USB_DIR_IN:
+ endpoint->tx_transferSize = endpoint_map->transferSize[hs];
+ endpoint->last = 0;
+ endpoint->tx_urb = NULL;
+ break;
+
+ case USB_DIR_OUT:
+ endpoint->rcv_transferSize = endpoint_map->transferSize[hs];
+ endpoint->rcv_urb = NULL;
+ break;
+ }
+
+ TRACE_MSG4(USBD, "endpoint[%d]: %x wMaxPacketSize: %02x %02x",
+ hs, endpoint,
+ endpoint->wMaxPacketSize[hs],
+ endpoint_map->wMaxPacketSize[hs]);
+ }
+
+ }
+ bus->ep0->endpoint_map_array->endpoint = bus->endpoint_array;
+
+ /* endpoint zero has no wMaxPacketSize set yet... so alloc buffer separately */
+ THROW_UNLESS(bus->ep0_urb = usbd_alloc_urb((struct usbd_function_instance *) bus->ep0, 0, 0, NULL), error);
+ THROW_UNLESS((bus->ep0_urb->buffer = (u8 *)KMALLOC (512)), error);
+
+ rc = 0;
+ CATCH(error) {
+ if (simple_instance)
+ usbp_dealloc_simple_instance(simple_instance);
+ if (composite_instance)
+ usbp_dealloc_composite_instance(composite_instance);
+
+ }
+ otg_sem_post(&usbd_bus_sem);
+ return rc;
+}
+
+#if 0
+/*!
+ * usbd_enable_function_irq () -
+ *
+ * @param bus
+ * @param arg
+ * @param serial_number
+ */
+int usbd_enable_function_irq (struct usbd_bus_instance *bus, char *arg, char *serial_number)
+{
+ return usbd_enable_function(bus, arg, serial_number);
+}
+#endif
+
+/*!
+ * usbd_function_disable() - disable a usbd function driver
+ * @param function
+ */
+void usbd_function_disable (struct usbd_function_instance *function)
+{
+ //int configuration;
+ struct usbd_function_driver *function_driver = function->function_driver;
+ /*
+ * XXX composite or class?
+ */
+ TRACE_MSG3(USBD, "DISABLE: function_instance: %x function_driver: %x function_disable: %x",
+ function, function_driver,
+ function_driver ? function_driver->fops->function_disable : NULL
+ );
+ /*
+ if (function_driver && function->function_driver->fops->function_disable)
+ function->function_driver->fops->function_disable (function);
+ */
+
+
+ function->bus = NULL;
+ function_driver->flags &= ~FUNCTION_ENABLED;
+}
+
+
+/*!
+ * usbd_disable_function() - called to disable the current function
+ *
+ * Used by a USB Bus interface driver to destroy a virtual device.
+ *
+ * @param bus
+ */
+void usbd_disable_function (struct usbd_bus_instance *bus)
+{
+ TRACE_MSG0(USBD, "DISABLE-");
+
+ // prevent any more bottom half scheduling
+ while (otg_sem_wait(&usbd_bus_sem));
+ bus->status = USBD_CLOSING;
+ TRACE_MSG1(USBD, "bus->status: %d", bus->status);
+ otg_sem_post(&usbd_bus_sem);
+
+
+ if (bus->function_instance) {
+
+ TRACE_MSG0(USBD, "disable function");
+ usbd_function_disable (bus->function_instance);
+ bus_function_disable (bus, bus->function_instance);
+ usbp_dealloc_composite_instance((struct usbd_composite_instance *)bus->function_instance);
+
+ if (bus->ep0_urb->buffer) LKFREE(bus->ep0_urb->buffer);
+ LKFREE (bus->ep0_urb);
+ bus->ep0_urb = NULL;
+
+ while (otg_sem_wait(&usbd_bus_sem));
+ bus->function_instance = NULL;
+ otg_sem_post(&usbd_bus_sem);
+ }
+ bus->status = USBD_CLOSED;
+ TRACE_MSG1(USBD, "bus->status: %d", bus->status);
+
+ /* XXX wait for reset, resume and suspend bh's */
+ // XXX
+}
+
+OTG_EXPORT_SYMBOL(usbd_enable_function);
+OTG_EXPORT_SYMBOL(usbd_disable_function);
+
+/* ************************************************************************** */
+/* ************************************************************************** */
+/*!
+ * @brief usbd_device_reset() - tell function driver it has been reset
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param data - bus instance pointer
+ * @return non-zero if errror
+ */
+void *usbd_device_reset(void *data)
+{
+ struct usbd_bus_instance *bus = (struct usbd_bus_instance *) data;
+ struct usbd_function_instance *function = bus->function_instance;
+ BOOL rc;
+
+ TRACE_MSG0(USBD, "DEVICE_RESET AAAA");
+
+ RETURN_NULL_UNLESS(bus->device_state == STATE_CONFIGURED);
+ RETURN_NULL_UNLESS(bus->status == USBD_OK);
+
+ /* Call all function drivers reset() operation and if available any
+ * interface drivers reset() operations.
+ */
+ rc = function->function_driver->fops->reset && function->function_driver->fops->reset (function);
+
+ TRACE_MSG0(USBD, "DEVICE_RESET BBBB");
+ TRACE_MSG1(USBD, "type: %d", function->function_type);
+
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ int i;
+
+ TRACE_MSG1(USBD, "interfaces: %d", composite_instance->interface_functions);
+ TRACE_MSG0(USBD, "DEVICE_RESET CCCC");
+ rc = class_instance &&
+ class_instance->function.function_driver->fops->reset &&
+ class_instance->function.function_driver->fops->reset ((struct usbd_function_instance *)class_instance);
+
+ TRACE_MSG0(USBD, "DEVICE_RESET DDDD");
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+ rc = interface_instance &&
+ interface_instance->function.function_driver->fops->reset &&
+ interface_instance->function.function_driver->fops->reset
+ ((struct usbd_function_instance *)interface_instance);
+ }
+ TRACE_MSG0(USBD, "DEVICE_RESET EEEE");
+ }
+ TRACE_MSG0(USBD, "DEVICE_RESET FFFF");
+ return NULL;
+}
+
+/*!
+ * @brief device_suspended() - tell function driver it has been suspended
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param data - bus instance pointer
+ * @return non-zero if errror
+ */
+void *usbd_device_suspended(void *data)
+{
+ struct usbd_bus_instance *bus = (struct usbd_bus_instance *) data;
+ struct usbd_function_instance *function = bus->function_instance;
+ BOOL rc;
+
+ TRACE_MSG2(USBD, "device_state: %d status: %d",bus->device_state, bus->status);
+
+ RETURN_NULL_UNLESS(bus->device_state == STATE_SUSPENDED);
+ RETURN_NULL_UNLESS(bus->status == USBD_OK);
+
+ /* Call all function drivers suspended() operation and if available any
+ * interface drivers suspended() operations.
+ */
+ rc = function->function_driver->fops->suspended && function->function_driver->fops->suspended (function);
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ int i;
+ rc = class_instance &&
+ class_instance->function.function_driver->fops->suspended &&
+ class_instance->function.function_driver->fops->suspended
+ ((struct usbd_function_instance *)class_instance);
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+ rc = interface_instance &&
+ interface_instance->function.function_driver->fops->suspended &&
+ interface_instance->function.function_driver->fops->suspended
+ ((struct usbd_function_instance *)interface_instance);
+ }
+ }
+ return NULL;
+}
+
+/*!
+ * @brief device_resumed() - tell function driver it has been resumed
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param data - bus instance pointer
+ * @return non-zero if errror
+ */
+void *usbd_device_resumed(void *data)
+{
+ struct usbd_bus_instance *bus = (struct usbd_bus_instance *) data;
+ struct usbd_function_instance *function = bus->function_instance;
+ BOOL rc;
+
+ TRACE_MSG1(USBD, "type: %d", function->function_type);
+
+ RETURN_NULL_UNLESS(bus->device_state == STATE_CONFIGURED);
+ RETURN_NULL_UNLESS(bus->status == USBD_OK);
+
+ /* Call all function drivers resumed() operation and if available any
+ * interface drivers resumed() operations.
+ */
+ rc = function->function_driver->fops->resumed && function->function_driver->fops->resumed (function);
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ int i;
+ rc = class_instance &&
+ class_instance->function.function_driver->fops->resumed &&
+ class_instance->function.function_driver->fops->resumed
+ ((struct usbd_function_instance *)class_instance);
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+ rc = interface_instance &&
+ interface_instance->function.function_driver->fops->resumed &&
+ interface_instance->function.function_driver->fops->resumed
+ ((struct usbd_function_instance *)interface_instance);
+ }
+ }
+ return NULL;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Device Request Processing:
+ * usbd_device_request()
+ *
+ */
+
+/*!
+ * @name C.f. 9.4 Standard Requests and table 9.3
+ *
+ * Encode valid requests into a bitmap for each recipient type for
+ * both directions. This is needed because there are some requests
+ * that appear to be a standard request but are not described in
+ * Chapter nine. For example the mouse driver hid request.
+ *
+ * So we only process the actual set of standard requests that are
+ * defined in chapter nine and even if it appears to be standard but
+ * is not in chapter nine then we pass it to the function driver.
+ *
+ */
+
+#define STD(x) (1<<(x+1))
+
+/*! @{ */
+/*! h2d_standard_requests and d2h_standard_requests
+ *
+ * These tables list all of the valid Chapter Nine requests. Any request
+ * not listed in these tables will NOT be processed by the EP0 function and
+ * will instead be passed to the appropriate function driver.
+ */
+u32 h2d_standard_requests[4] = {
+ // 0 - Device
+ STD(USB_REQ_CLEAR_FEATURE) |
+ STD(USB_REQ_SET_FEATURE) |
+ STD(USB_REQ_SET_ADDRESS) |
+ STD(USB_REQ_SET_DESCRIPTOR) |
+ STD(USB_REQ_SET_CONFIGURATION) ,
+ // 1 - Interface
+ STD(USB_REQ_CLEAR_FEATURE) |
+ STD(USB_REQ_SET_FEATURE) |
+ STD(USB_REQ_SET_INTERFACE) ,
+ // 2 - Endpoint
+ STD(USB_REQ_CLEAR_FEATURE) |
+ STD(USB_REQ_SET_FEATURE) ,
+ // 3 - Other
+ 0,
+};
+
+u32 d2h_standard_requests[4] = {
+ // 0 - Device
+ STD(USB_REQ_GET_STATUS) |
+ STD(USB_REQ_GET_DESCRIPTOR) |
+ STD(USB_REQ_GET_CONFIGURATION),
+ // 1 - Interface
+ STD(USB_REQ_GET_STATUS) |
+ STD(USB_REQ_GET_INTERFACE) ,
+ // 2 - Endpoint
+ STD(USB_REQ_GET_STATUS) |
+ STD(USB_REQ_SYNCH_FRAME) ,
+ // 3 - Other
+ 0,
+};
+
+/* @} */
+
+
+/*!
+ * @brief devreq_set_configuration() - process a received urb
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param function
+ * @param wValue
+ * @return non-zero if errror
+ */
+static int devreq_set_configuration(struct usbd_function_instance *function, int wValue)
+{
+ struct usbd_bus_instance *bus = function->bus;
+ BOOL rc;
+
+ // c.f. 9.4.7 - the top half of wValue is reserved
+ // c.f. 9.4.7 - zero is the default or addressed state, in our case this
+ // is the same is configuration zero, but will be fixed in usbd.c when used.
+
+ u8 ConfigurationValue = wValue & 0x7f;
+
+ //RETURN_EINVAL_IF(ConfigurationValue > bNumConfigurations);
+ ///* A ConfigurationValue of zero is for the default configuration (i.e.) the
+ // * first one. A non-zero value needs to be decremented to allow it to be used
+ // * as an index.
+ // */
+ //ConfigurationValue = !ConfigurationValue ? 0 : ConfigurationValue -1;
+
+
+ /* XXX check configuration value is correct, look in pre-built descriptors
+ * RETURN_EINVAL_UNLESS(configuration == ...)
+ */
+
+
+ /* propagate event */
+ //XXX pcd_bus_event_handler_irq (function->bus, ConfigurationValue ? DEVICE_CONFIGURED : DEVICE_DE_CONFIGURED, 0);
+
+ if (bus->driver->bops->set_configuration) bus->driver->bops->set_configuration (bus, ConfigurationValue);
+
+ TRACE_MSG1(USBD, "type: %d", function->function_type);
+
+ /* Call all function drivers set_configuration() operation and if available any
+ * interface drivers set_configuration() operations.
+ */
+ if (function_simple == function->function_type) {
+
+ struct usbd_simple_instance *simple_instance = (struct usbd_simple_instance *)function;
+
+ /* save ConfigurationValue for GET_CONFIGURATION request
+ */
+ simple_instance->ConfigurationValue = wValue & 0x7f;//ConfigurationValue;
+
+ }
+
+ rc = function->function_driver->fops->set_configuration &&
+ function->function_driver->fops->set_configuration (function, wValue);
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_class_instance *class_instance = composite_instance->class_instance;
+ int i;
+
+ /* save ConfigurationValue for GET_CONFIGURATION request, call
+ * the reset function for the class and all interface functins.
+ */
+ composite_instance->ConfigurationValue = wValue & 0x7f;//ConfigurationValue;
+
+ rc = class_instance &&
+ class_instance->function.function_driver->fops->set_configuration &&
+ class_instance->function.function_driver->fops->set_configuration
+ ((struct usbd_function_instance *)class_instance, wValue);
+
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+ rc = interface_instance &&
+ interface_instance->function.function_driver->fops->set_configuration &&
+ interface_instance->function.function_driver->fops->set_configuration
+ ((struct usbd_function_instance *)interface_instance, wValue);
+ }
+ }
+ return 0;
+}
+
+/*!
+ * @brief find_interface_instance()
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param composite_instance
+ * @param wIndex
+ * @return interface_instance
+ */
+static struct usbd_interface_instance*
+find_interface_instance(struct usbd_composite_instance *composite_instance, int wIndex)
+{
+ int i;
+ TRACE_MSG1(USBD, "wIndex: %d", wIndex);
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+ struct usbd_interface_driver *interface_driver =
+ (struct usbd_interface_driver *) interface_instance->function.function_driver;
+
+ TRACE_MSG3(USBD, "wIndex: %d interface->wIndex: %d, interface->interfaces: %d",
+ wIndex, interface_instance->wIndex, interface_driver->interfaces);
+
+ CONTINUE_UNLESS((wIndex >= interface_instance->wIndex) &&
+ (wIndex < interface_instance->wIndex + interface_driver->interfaces) );
+
+ TRACE_MSG2(USBD, "found interface: %d altSetting: %d",
+ interface_instance->wIndex, interface_instance->altsetting);
+ return interface_instance;
+ }
+ TRACE_MSG0(USBD, "ERROR");
+ return NULL;
+}
+
+/*!
+ * @brief devreq_set_interface_altsetting() -
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param function
+ * @param wIndex
+ * @param wValue
+ * @return non-zero if errror
+ */
+static int devreq_set_interface_altsetting(struct usbd_function_instance *function, int wIndex, int wValue)
+{
+ //struct usbd_bus_instance *bus = function->bus;
+ BOOL rc;
+
+ TRACE_MSG2(USBD, "Interface/wIndex: %02x altsetting/wValue: %02x", wIndex, wValue);
+
+ // XXX pcd_bus_event_handler_irq (bus, DEVICE_SET_INTERFACE, 0);
+
+ /* Call function driver set_interface() operation and if available any
+ * interface drivers set_interface() operations.
+ */
+ if (function_simple == function->function_type) {
+ struct usbd_simple_instance *simple_instance = (struct usbd_simple_instance *)function;
+ RETURN_EINVAL_IF(wIndex > simple_instance->interfaces);
+ simple_instance->altsettings[wIndex] = (u8) wValue;
+ rc = simple_instance->function.function_driver->fops->set_interface &&
+ simple_instance->function.function_driver->fops->set_interface (function, wIndex, wValue);
+ }
+
+ /* Note that composite and class drivers does not receive set_interface information
+ */
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_interface_instance *interface_instance;
+ RETURN_EINVAL_UNLESS((interface_instance = find_interface_instance(composite_instance, wIndex)));
+
+ rc = interface_instance->function.function_driver->fops->set_interface &&
+ interface_instance->function.function_driver->fops->set_interface
+ ((struct usbd_function_instance *)interface_instance, wIndex, wValue);
+ }
+ return 0;
+}
+
+
+
+/*!
+ * @brief do_endpoint_cleared - clear endpoint
+ *
+ * Set or clear endpoint.
+ *
+ * Call bus interface halt_endpoint to set or clear.
+ *
+ * Call interface function endpoint clear to indicate clear.
+ *
+ * Return non-zero to indicate failure.
+ * @param function
+ * @param bEndpointAddress
+ * @param setclear_flag
+ * @return int
+ */
+static int do_endpoint_cleared (struct usbd_function_instance *function, int bEndpointAddress, int setclear_flag)
+{
+ u16 endpoint_index;
+ //int endpoints;
+ //int i;
+ struct usbd_endpoint_instance *endpoint;
+
+ TRACE_MSG2(USBD, "bEndpointAddress: %04x setclear: %x", bEndpointAddress, setclear_flag);
+
+ /* Find and verify the endpoint map index as well. Then clear the
+ * endpoint using the bus interface driver halt_endpoint()
+ * operation, if that is ok, then inform the function driver using
+ * the endpoint_cleared() operation.
+ */
+ RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(function->bus, bEndpointAddress))
+ < function->bus->endpoints);
+ TRACE_MSG1(USBD, "endpoint_index: %x", endpoint_index);
+
+ RETURN_EINVAL_UNLESS((endpoint = function->bus->endpoint_array + endpoint_index));
+
+ RETURN_EINVAL_IF(function->bus->driver->bops->halt_endpoint(function->bus, bEndpointAddress, setclear_flag));
+ TRACE_MSG0(USBD, "halt_endpoint OK");
+
+ RETURN_ZERO_IF(setclear_flag);
+
+ if (function_simple == function->function_type) {
+ struct usbd_simple_instance *simple_instance = (struct usbd_simple_instance *)function;
+ TRACE_MSG2(USBD, "SIMPLE bEndpointAddress: %0dx interfaces: %02d", bEndpointAddress, simple_instance->interfaces);
+ //RETURN_EINVAL_IF(wIndex > simple_instance->interfaces);
+ if (simple_instance->function.function_driver->fops->endpoint_cleared)
+ simple_instance->function.function_driver->fops->endpoint_cleared (function, endpoint_index);
+ }
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ struct usbd_interface_instance *interface_instance;
+ TRACE_MSG2(USBD, "COMPOSITE endpoint->interface_wIndex: %0d find: %x",
+ endpoint->interface_wIndex,
+ find_interface_instance(composite_instance, endpoint->interface_wIndex));
+
+ RETURN_EINVAL_UNLESS((interface_instance =
+ find_interface_instance(composite_instance, endpoint->interface_wIndex)));
+ TRACE_MSG1(USBD, "interface_instance: %x", interface_instance);
+
+ if (interface_instance->function.function_driver->fops->endpoint_cleared)
+ interface_instance->function.function_driver->fops->endpoint_cleared
+ ((struct usbd_function_instance *)interface_instance, bEndpointAddress);
+ }
+ return 0;
+}
+
+
+/*!
+ * @brief do_non_standard_device_request - process a device request
+ *
+ * Process a received device request. If not a Chapter nine request pass it
+ * to the other loaded function driver device_request() function.
+ *
+ * @param function
+ * @param request
+ @return int Return non-zero to indicate failure.
+ */
+static int do_non_standard_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ struct usbd_class_instance *class_instance;
+ struct usbd_interface_instance *interface_instance;
+ u8 bmRequestType = request->bmRequestType;
+ //u16 wValue = le16_to_cpu(request->wValue);
+ u16 wIndex = le16_to_cpu(request->wIndex);
+ //u16 wLength = le16_to_cpu(request->wLength);
+
+ u16 endpoint_index;
+ //int endpoints;
+ //int i;
+
+ if (function_simple == function->function_type) {
+ struct usbd_simple_instance *simple_instance = (struct usbd_simple_instance *)function;
+ return simple_instance->function.function_driver->fops->device_request ?
+ simple_instance->function.function_driver->fops->device_request(function, request) : 0;
+ }
+
+ if (function_composite == function->function_type) {
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+ int i;
+
+ /* direct request to simple, interface or composite driver as appropriate
+ */
+ switch(bmRequestType & USB_REQ_RECIPIENT_MASK) {
+ case USB_REQ_RECIPIENT_ENDPOINT:
+
+ /* Need to find interface function responsible for endpoint and and give
+ * it the device request.
+ */
+ RETURN_EINVAL_UNLESS((endpoint_index = usbd_find_endpoint_index(function->bus, wIndex))
+ < function->bus->endpoints);
+
+ wIndex = function->bus->endpoint_array[endpoint_index].interface_wIndex;
+
+ /* FALL THROUGH */
+
+ case USB_REQ_RECIPIENT_INTERFACE:
+
+ /* Determine interface function responsible for interface and give it
+ * the device request.
+ */
+ RETURN_EINVAL_UNLESS((interface_instance = find_interface_instance(composite_instance, wIndex)));
+
+ return interface_instance->function.function_driver->fops->device_request ?
+ (interface_instance->function.function_driver->fops->device_request
+ ((struct usbd_function_instance *)interface_instance, request)) :
+ -EINVAL;
+ /*
+ * XXX It may be necessary to allow all failed requests above
+ * to fall through to here....
+ */
+ case USB_REQ_RECIPIENT_DEVICE:
+ case USB_REQ_RECIPIENT_OTHER:
+ default:
+ /* Attempt to find someone who will handle this request ...
+ * As long as each interface properly fails requests it does
+ * not understand there is a reasonably good chance that it
+ * will get to the correct handler.
+ */
+ RETURN_ZERO_UNLESS((class_instance = composite_instance->class_instance) &&
+ class_instance->function.function_driver->fops->device_request ?
+ class_instance->function.function_driver->fops->device_request
+ ((struct usbd_function_instance *)class_instance, request) : -EINVAL);
+
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array + i;
+
+ TRACE_MSG2(USBD, "interface_instance: %x %s",
+ interface_instance,
+ interface_instance ? interface_instance->function.name : "");
+
+ RETURN_ZERO_UNLESS(interface_instance &&
+ interface_instance->function.function_driver->fops->device_request ?
+ (interface_instance->function.function_driver->fops->device_request
+ ((struct usbd_function_instance *)interface_instance, request)) :
+ -EINVAL);
+ }
+ return -EINVAL;
+ }
+ }
+ return -EINVAL; /* never used */
+}
+
+/*!
+ * @brief devreq_get_device_feature_settings() - process a received urb
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param function
+ * @return non-zero if errror
+ */
+int devreq_get_device_feature_settings(struct usbd_function_instance *function)
+{
+ struct usbd_bus_instance *bus = function->bus;
+ return bus->device_feature_settings;
+}
+
+
+/*!
+ * @brief devreq_device_feature - handle set/clear feature requests for device
+ * Used by the USB Device Core to set/clear endpoint halt status.
+ *
+ * We assume that if the udc driver does not implement anything then
+ * we should just return zero for ok.
+
+ * @param bus
+ * @param features
+ * @param flag
+ * @return int
+ */
+int devreq_device_feature (struct usbd_bus_instance *bus, int features, int flag)
+{
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ //u32 features = bus->device_feature_settings;
+
+ TRACE_MSG0(USBD, "BUS_DEVICE FEATURE");
+
+ /* if USB Host has enabled remote wakeup
+ */
+ if (features & FEATURE(USB_DEVICE_REMOTE_WAKEUP) )
+ otg_queue_event(pcd->otg,
+ flag ? REMOTE_WAKEUP_ENABLED : REMOTE_WAKEUP_ENABLED_, USBD,
+ flag ? "DEVICE FEATURE - REMOTE_WAKEUP_ENABLED" :
+ "DEVICE FEATURE - REMOTE_WAKEUP_ENABLED/");
+
+ /* not legal to clear HNP */
+ if (flag) {
+
+ /* if A-Device has enabled hnp
+ */
+ if (features & FEATURE(USB_OTG_B_HNP_ENABLE) )
+ otg_queue_event(pcd->otg, HNP_ENABLED, USBD, "DEVICE FEATURE - B_HNP_ENABLE");
+
+ /* if A-Device does not support HNP on this port
+ */
+ else if ( features & FEATURE(USB_OTG_A_ALT_HNP_ENABLE) )
+ ; //otg_event(pcd->otg, not(a_hnp_support));
+
+ /* if A-Device does support HNP
+ */
+ else if (features & FEATURE(USB_OTG_A_HNP_SUPPORT))
+ ; //otg_event(pcd->otg, a_hnp_support);
+
+ }
+ return 0;
+}
+
+/*!
+ * @brief copy_config() - copy data into buffer
+
+ * @param cp
+ * @param data
+ * @param actual_length
+ * @param max_buf
+ * @return length
+ */
+static int copy_config (u8 *cp, void *data, int actual_length, int max_buf)
+{
+ int available = max_buf - actual_length;
+ int length = MIN(*(u8 *)data, available);
+ RETURN_ZERO_UNLESS (data);
+ RETURN_ZERO_UNLESS (length);
+ memcpy (cp, data, length);
+ return length;
+}
+
+#if 0
+/*!
+ * @brief copy_endpoint() - copy data into buffer
+ * @param function
+ * @param cp
+ * @param endpoint
+ * @param endpoint_index
+ * @param actual_length
+ * @param max_buf
+ * @param hs
+ * @return length
+ */
+static int copy_endpoint (struct usbd_function_instance *function, u8 *cp,
+ struct usbd_endpoint_descriptor *endpoint, int endpoint_index, int actual_length, int max_buf, int hs)
+{
+ int available = max_buf - actual_length;
+ int length = MIN(endpoint->bLength, available);
+ struct usbd_endpoint_descriptor endpoint_copy;
+
+ RETURN_ZERO_IF (!length);
+ memcpy (&endpoint_copy, endpoint, endpoint->bLength);
+ usbd_endpoint_update(function, endpoint_index, &endpoint_copy, hs);
+ memcpy (cp, &endpoint_copy, length);
+ return length;
+}
+#endif
+
+/*!
+ * @brief usbd_get_descriptor() - copy a descriptor into buffer
+ *
+ * @param function
+ * @param buffer
+ * @param wLength
+ * @param descriptor_type
+ * @param index
+ * @return non-zero for error.
+ */
+int usbd_get_descriptor (struct usbd_function_instance *function, u8 *buffer, int wLength, int descriptor_type, int index)
+{
+ struct usbd_bus_instance *bus = function->bus;
+ struct usbd_function_driver *function_driver = bus->function_instance->function_driver;
+ int actual_length = 0;
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //TRACE_MSG3(USBD, "descriptor_type: %d index: %d wLength: %d", descriptor_type, index, wLength);
+ switch (descriptor_type) {
+ case USB_DT_DEVICE: {
+ struct usbd_device_descriptor device_descriptor;
+ struct usbd_device_description *device_description;
+ u8 iManufacturer;
+ u8 iProduct;
+ u8 iSerialNumber = 0;
+
+ //printk(KERN_INFO"%s: DT_DEVICE\n", __FUNCTION__);
+ //TRACE_MSG1(USBD, "DT_DEVICE function_type: %d", function->function_type);
+
+ if (function_simple == function->function_type) {
+ struct usbd_simple_driver * simple_driver =
+ (struct usbd_simple_driver *)function_driver;
+
+ device_description = simple_driver->device_description;
+ iManufacturer = simple_driver->iManufacturer;
+ iProduct = simple_driver->iProduct;
+ iSerialNumber = simple_driver->iSerialNumber;
+ }
+ else if (function_composite == function->function_type) {
+
+ struct usbd_composite_driver * composite_driver =
+ (struct usbd_composite_driver *)function_driver;
+
+ device_description = composite_driver->device_description;
+
+ //TRACE_MSG6(USBD, "iManufacturer: %d iProduct: %d iSerialNumber: %d "
+ // "idVendor: %04x idProduct: %04x bcdDevice: %04x",
+ // composite_driver->iManufacturer, composite_driver->iProduct,
+ // composite_driver->iSerialNumber, device_description->idVendor,
+ // device_description->idProduct, device_description->bcdDevice
+ // );
+
+ iManufacturer = composite_driver->iManufacturer;
+ iProduct = composite_driver->iProduct;
+ iSerialNumber = composite_driver->iSerialNumber;
+ }
+ else
+ return 0;
+
+ //TRACE_MSG3(USBD, "function_driver: %x device_descriptor: %x %d",
+ // function_driver, device_descriptor, device_descriptor->bLength);
+
+ RETURN_EINVAL_UNLESS(device_description);
+
+ // correct the correct control endpoint 0 wLength packet size into the descriptor
+ //device_descriptor = (struct usbd_device_descriptor *) buffer;
+
+ device_descriptor.bLength = sizeof(struct usbd_device_descriptor);;
+ device_descriptor.bDescriptorType = USB_DT_DEVICE;
+ device_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
+ device_descriptor.bDeviceClass = device_description->bDeviceClass;
+ device_descriptor.bDeviceSubClass = device_description->bDeviceSubClass;
+ device_descriptor.bDeviceProtocol = device_description->bDeviceProtocol;
+ device_descriptor.bMaxPacketSize0 = device_description->bMaxPacketSize0;
+
+ device_descriptor.idVendor = cpu_to_le16(device_description->idVendor);
+ device_descriptor.idProduct = cpu_to_le16(device_description->idProduct);
+ device_descriptor.bcdDevice = cpu_to_le16(device_description->bcdDevice);
+ device_descriptor.iManufacturer = iManufacturer;
+ device_descriptor.iProduct = iProduct;
+ device_descriptor.iSerialNumber = iSerialNumber;
+ device_descriptor.bNumConfigurations = 1;
+
+ //actual_length += wLength;
+ //memcpy(buffer, device_descriptor, wLength);
+
+ /* copy device descriptor for this device */
+ actual_length += copy_config (buffer + actual_length, &device_descriptor, actual_length, wLength);
+ break;
+ }
+
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ case USB_DT_DEVICE_QUALIFIER: {
+ // c.f. 9.6.2 Device Qualifier
+ struct usbd_device_qualifier_descriptor device_qualifier_descriptor;
+
+ struct usbd_device_description *device_description;
+
+ //printk(KERN_INFO"%s: DT_DEVICE_QUALIFIER\n", __FUNCTION__);
+
+ if (function_simple == function->function_type) {
+ struct usbd_simple_driver * simple_driver =
+ (struct usbd_simple_driver *)function_driver;
+
+ device_description = simple_driver->device_description;
+ }
+ else if (function_composite == function->function_type) {
+
+ struct usbd_composite_driver * composite_driver =
+ (struct usbd_composite_driver *)function_driver;
+
+ device_description = composite_driver->device_description;
+ }
+ else
+ return 0;
+
+ device_qualifier_descriptor.bLength = sizeof(struct usbd_device_qualifier_descriptor);;
+ device_qualifier_descriptor.bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ device_qualifier_descriptor.bcdUSB = __constant_cpu_to_le16(USB_BCD_VERSION);
+ device_qualifier_descriptor.bDeviceClass = device_description->bDeviceClass;
+ device_qualifier_descriptor.bDeviceSubClass = device_description->bDeviceSubClass;
+ device_qualifier_descriptor.bDeviceProtocol = device_description->bDeviceProtocol;
+ device_qualifier_descriptor.bMaxPacketSize0 = device_description->bMaxPacketSize0;
+ device_qualifier_descriptor.bNumConfigurations = 1; // XXX
+ device_qualifier_descriptor.bReserved = 0;
+
+ // copy descriptor for this device
+ actual_length += copy_config (buffer + actual_length, &device_qualifier_descriptor, actual_length, wLength);
+
+ break;
+ }
+
+ case USB_DT_OTHER_SPEED_CONFIGURATION:
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+
+ case USB_DT_CONFIGURATION: {
+ //printk(KERN_INFO"%s: DT_CONFIGURATION\n", __FUNCTION__);
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ int hs = bus->high_speed ? descriptor_type == USB_DT_CONFIGURATION:
+ descriptor_type == USB_DT_OTHER_SPEED_CONFIGURATION;
+ #else /* CONFIG_OTG_HIGH_SPEED */
+ int hs = 0;
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+ struct usbd_configuration_descriptor *configuration_descriptor;
+
+ if (function_simple == function->function_type)
+ configuration_descriptor = ((struct usbd_simple_instance *)function)->configuration_descriptor[hs];
+ else if (function_composite == function->function_type)
+ configuration_descriptor = ((struct usbd_composite_instance *)function)->configuration_descriptor[hs];
+ else
+ return 0;
+
+ actual_length = MIN(le16_to_cpu(configuration_descriptor->wTotalLength), wLength);
+ memcpy (buffer, configuration_descriptor, actual_length);
+
+ /* set descriptor type to requested type */
+ configuration_descriptor = (struct usbd_configuration_descriptor *)buffer;
+ configuration_descriptor->bDescriptorType = descriptor_type;
+
+ #if 0
+ {
+ u8 * cp = buffer;
+
+ int i;
+ int size = configuration_descriptor->wTotalLength;
+
+ TRACE_MSG2(USBD, "cfg: %x size: %d", cp, size);
+ for (i = 0; i < size; i+= 8) {
+ TRACE_MSG8(USBD, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+
+ }
+ #endif
+ break;
+ }
+
+ case USB_DT_STRING: {
+ struct usbd_string_descriptor *string_descriptor = NULL;
+ //printk(KERN_INFO"%s: DT_STRING\n", __FUNCTION__);
+ RETURN_EINVAL_IF (!(string_descriptor = usbd_get_string_descriptor (function, (u8)index)));
+ actual_length += copy_config (buffer + actual_length, string_descriptor, actual_length, wLength);
+ break;
+ }
+
+ case USB_DT_OTG: {
+ struct usbd_otg_descriptor *otg_descriptor = (struct usbd_otg_descriptor *) buffer;
+ actual_length += sizeof(struct usbd_otg_descriptor);
+ otg_descriptor->bLength = sizeof(struct usbd_otg_descriptor);
+ otg_descriptor->bDescriptorType = USB_DT_OTG;
+ otg_descriptor->bLength = 0; // XXX
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ return actual_length;
+}
+
+
+
+
+/*!
+ * @brief setclear() - set or reset a bit in a mask
+ * @param features
+ * @param flag
+ * @param value
+ */
+static void setclear(u32 *features, u32 flag, u32 value)
+{
+ if (value) *features |= flag;
+ else *features &= ~flag;
+}
+
+/*!
+ * @brief do_standard_device_request - process a device request
+ *
+ * Process a received device request. If not a Chapter nine request pass it
+ * to the other loaded function driver device_request() function.
+ *
+ * @param function
+ * @param request
+ * @return non-zero to indicate failure.
+ */
+static int do_standard_device_request (struct usbd_function_instance *function, struct usbd_device_request *request)
+{
+ struct usbd_simple_instance *simple_instance = (struct usbd_simple_instance *)function;
+ struct usbd_composite_instance *composite_instance = (struct usbd_composite_instance *)function;
+
+ struct usbd_bus_instance *bus = function->bus;
+ u8 bRequest = request->bRequest;
+ u8 bmRequestType = request->bmRequestType;
+ u16 wValue = le16_to_cpu(request->wValue);
+ u16 wIndex = le16_to_cpu(request->wIndex);
+ u16 wLength = le16_to_cpu(request->wLength);
+ //u16 endpoint_index;
+ struct pcd_instance *pcd = (struct pcd_instance *)bus->privdata;
+ usbd_device_state_t device_state = usbd_get_device_state(function);
+
+
+ switch (device_state) {
+ case STATE_CREATED:
+ case STATE_ATTACHED:
+ case STATE_POWERED:
+ printk(KERN_INFO"%s: bad device state: %d\n", __FUNCTION__, device_state);
+ TRACE_MSG1(USBD, "bad device_state: %d", device_state);
+ return -EINVAL;
+
+ case STATE_INIT:
+ case STATE_DEFAULT:
+ switch (bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_GET_INTERFACE:
+ case USB_REQ_GET_STATUS:
+ case USB_REQ_SET_DESCRIPTOR:
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SYNCH_FRAME:
+ printk(KERN_INFO"%s: bad request %d for this device state: %d\n", __FUNCTION__,
+ bRequest, device_state);
+ TRACE_MSG2(USBD, "bad request: %d for this device_state: %d", bRequest, device_state);
+ return -EINVAL;
+
+ case USB_REQ_GET_CONFIGURATION:
+ case USB_REQ_SET_ADDRESS:
+ otg_queue_event(pcd->otg, DEVICE_REQUEST, USBD, "DEVICE REQUEST");
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_SET_FEATURE:
+ break;
+ }
+ case STATE_ADDRESSED:
+ case STATE_CONFIGURED:
+ case STATE_SUSPENDED:
+ break;
+ case STATE_UNKNOWN:
+ TRACE_MSG1(USBD, "unknown device_state: %d", device_state);
+ return -EINVAL;
+ }
+
+ /* Handle all requests that return data (direction bit set on bm RequestType).
+ * N.B. no Chapter 9 requests send additional data.
+ */
+ if ((bmRequestType & USB_REQ_DIRECTION_MASK)) {
+ struct usbd_urb *urb;
+ int rc = 1;
+ switch (bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_ADDRESS:
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_SET_DESCRIPTOR:
+ case USB_REQ_SET_FEATURE:
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SYNCH_FRAME:
+ printk(KERN_INFO"%s: bad direction\n", __FUNCTION__);
+ TRACE_MSG0(USBD, "bad direction");
+ return -EINVAL;
+ }
+
+ RETURN_EINVAL_IF(!wLength);
+
+ /* allocate urb, no notify callback, urb will be automatically de-allocated
+ */
+ RETURN_EINVAL_UNLESS((urb = bus->ep0_urb));
+ urb->status = USBD_URB_OK;
+ urb->irq_flags = 0;
+
+ switch (bRequest) {
+
+ case USB_REQ_GET_STATUS:
+ urb->actual_length = 2;
+ urb->buffer[0] = urb->buffer[1] = 0;
+
+ switch (bmRequestType & USB_REQ_RECIPIENT_MASK) {
+ case USB_REQ_RECIPIENT_DEVICE:
+
+ UNLESS (usbd_get_bMaxPower(function)) urb->buffer[0] |= USB_STATUS_SELFPOWERED;
+ urb->buffer[0] |=
+ (devreq_get_device_feature_settings(function) & FEATURE(USB_DEVICE_REMOTE_WAKEUP)) ?
+ USB_STATUS_REMOTEWAKEUP : 0;
+ TRACE_MSG2(USBD, "GET STATUS DEVICE status: %02x %02x",
+ urb->buffer[0], urb->buffer[1]);
+ rc = 0;
+ break;
+ case USB_REQ_RECIPIENT_ENDPOINT:
+ urb->buffer[0] = function->bus->driver->bops->endpoint_halted (bus,wIndex);
+ rc = 0;
+ TRACE_MSG2(USBD, "GET STATUS ENDPOINT status: %02x %02x", urb->buffer[0], urb->buffer[1]);
+ break;
+ case USB_REQ_RECIPIENT_INTERFACE:
+ TRACE_MSG2(USBD, "GET STATUS INTERFACE status: %02x %02x", urb->buffer[0], urb->buffer[1]);
+ rc = 0;
+ break;
+ case USB_REQ_RECIPIENT_OTHER:
+ TRACE_MSG0(USBD, "GET STATUS OTHER");
+ default:
+ TRACE_MSG0(USBD, "GET STATUS BAD RECIPIENT");
+ rc = -EINVAL;
+ }
+ break;
+
+ case USB_REQ_GET_DESCRIPTOR:
+ //printk(KERN_INFO"%s: GET DESCRIPTOR\n", __FUNCTION__);
+
+ TRACE_MSG2(USBD, "GET_DESCRIPTOR type : %04x, wLength: %d", wValue, wLength);
+
+ rc = usbd_get_descriptor (function, urb->buffer, wLength, wValue >> 8, wValue & 0xff);
+
+
+ //TRACE_MSG1(USBD, "get descriptor rc : %d", rc);
+ if (rc != -EINVAL) {
+ urb->actual_length = rc;
+ //printk(KERN_INFO"%s: length: %d\n", __FUNCTION__, rc);
+ rc = 0;
+ }
+ else {
+ //printk(KERN_INFO"%s: bad description\n", __FUNCTION__);
+ TRACE_MSG0(USBD, "bad description");
+ }
+ break;
+
+ case USB_REQ_GET_CONFIGURATION:
+ urb->actual_length = 1;
+ if (function_simple == function->function_type)
+ urb->buffer[0] = simple_instance->ConfigurationValue;
+
+ else if (function_composite == function->function_type)
+ urb->buffer[0] = composite_instance->ConfigurationValue;
+
+ TRACE_MSG1(USBD, "GET CONFIGURATION: %d", urb->buffer[0]);
+ rc=0; // XXX Always success???
+ break;
+
+ case USB_REQ_GET_INTERFACE:
+
+ if (function_simple == function->function_type) {
+ rc = 1;
+ BREAK_IF(wIndex > simple_instance->interfaces);
+ urb->buffer[0] = simple_instance->altsettings[wIndex];
+ urb->actual_length = 1;
+ rc = 0;
+ TRACE_MSG1(USBD, "GET INTERFACE: %d simple", urb->buffer[0]);
+ }
+ else if (function_composite == function->function_type) {
+ struct usbd_interface_instance *interface_instance;
+ if ((interface_instance = find_interface_instance(composite_instance, wIndex))) {
+ urb->buffer[0] = interface_instance->altsetting;
+ urb->actual_length = 1;
+ rc = 0;
+ TRACE_MSG1(USBD, "GET INTERFACE: %d composite", urb->buffer[0]);
+ }
+ else {
+ TRACE_MSG1(USBD, "GET INTERFACE: %d composite not found", urb->buffer[0]);
+
+ }
+ }
+ break;
+ default:
+ //printk(KERN_INFO"%s: bad descriptor\n", __FUNCTION__);
+ TRACE_MSG0(USBD, "bad descriptor type");
+ rc = 1;
+ }
+
+ if (!(urb->actual_length % usbd_endpoint_zero_wMaxPacketSize(function, usbd_high_speed(function))) &&
+ (urb->actual_length < wLength))
+ urb->flags |= USBD_URB_SENDZLP;
+
+ RETURN_ZERO_UNLESS(rc || usbd_start_in_urb(urb));
+ /* only get here if error */
+ usbd_free_urb(urb);
+ //TRACE_MSG0(USBD, "get failed");
+ TRACE_MSG1(USBD, "get failed rc:=%x",rc);
+ return -EINVAL;
+ }
+ /* Handle the requests that do not return data.
+ */
+ else {
+ int setclear_flag = USB_REQ_SET_FEATURE == bRequest;
+ switch (bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ TRACE_MSG4(USBD, "FEATURE: recipient: %d wIndex: %04x wValue: %04x setclear: %02x",
+ bmRequestType & USB_REQ_RECIPIENT_MASK, wIndex, wValue, setclear_flag);
+ switch (bmRequestType & USB_REQ_RECIPIENT_MASK) {
+ case USB_REQ_RECIPIENT_DEVICE:
+ //switch (wValue) {
+ // otg_event(pcd->otg, HNP_ENABLED, PCD, "DEVICE FEATURE - B_HNP_ENABLE");
+ // break;
+ //}
+
+ switch (wValue) {
+ case USB_OTG_A_ALT_HNP_ENABLE: // C.f. OTG 6.5.3
+ case USB_OTG_A_HNP_SUPPORT: // C.f. OTG 6.5.2
+ case USB_OTG_B_HNP_ENABLE: // C.f. OTG 6.5.1
+ TRACE_MSG0(USBD, "HNP");
+ RETURN_EINVAL_UNLESS(setclear_flag); // cleared by reset
+ break;
+ case USB_DEVICE_REMOTE_WAKEUP:
+ TRACE_MSG0(USBD, "REMOTE WAKEUP");
+ break;
+ case USB_TEST_MODE:
+ TRACE_MSG2(USBD, "TEST MODE selector: %04x %02x", wIndex, wIndex >> 8);
+ switch (wIndex >> 8) {
+ case USB_TEST_J:
+ case USB_TEST_K:
+ case USB_TEST_SE0_NAK:
+ case USB_TEST_PACKET: // XXX it should be possible to do this generically here
+ case USB_TEST_FORCE_ENABLE:
+ return function->bus->driver->bops->device_feature(
+ function->bus, wIndex >> 8, setclear_flag);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ setclear(&bus->device_feature_settings, FEATURE(wValue), setclear_flag);
+ return devreq_device_feature(bus, FEATURE(wValue), setclear_flag);
+
+ case USB_REQ_RECIPIENT_ENDPOINT:
+ RETURN_EINVAL_UNLESS(USB_ENDPOINT_HALT == wValue);
+
+ setclear(&(bus->endpoint_array[wIndex].feature_setting),
+ FEATURE(wValue), bRequest == USB_REQ_SET_FEATURE);
+
+ /* wIndex contains bEndpointAddress, also find and verify the endpoint map index as well.
+ * Then clear the endpoint using the bus interface driver halt_endpoint() operation,
+ * if that is ok, then inform the function driver using the endpoint_cleared() operation.
+ */
+ return do_endpoint_cleared(function, wIndex, setclear_flag);
+
+
+ case USB_REQ_RECIPIENT_INTERFACE:
+ case USB_REQ_RECIPIENT_OTHER:
+ default:
+ TRACE_MSG0(USBD, "bad recipient");
+ return -EINVAL;
+ }
+
+ case USB_REQ_SET_ADDRESS:
+ if (bus->driver->bops->set_address) bus->driver->bops->set_address (bus, wValue);
+ // XXX pcd_bus_event_handler_irq (bus, DEVICE_ADDRESS_ASSIGNED, wValue);
+ return 0;
+
+ case USB_REQ_SET_DESCRIPTOR:
+ TRACE_MSG0(USBD, "set descriptor not supported");
+ return -EINVAL;
+
+ case USB_REQ_SET_CONFIGURATION:
+ return devreq_set_configuration(function, wValue);
+
+ case USB_REQ_SET_INTERFACE:
+ return devreq_set_interface_altsetting(function, wIndex, wValue);
+
+ case USB_REQ_GET_CONFIGURATION:
+ case USB_REQ_GET_DESCRIPTOR:
+ case USB_REQ_GET_INTERFACE:
+ case USB_REQ_GET_STATUS:
+ case USB_REQ_SYNCH_FRAME:
+ TRACE_MSG0(USBD, "unkown");
+ return -EINVAL;
+ }
+ }
+ TRACE_MSG0(USBD, "not possible");
+ return -EINVAL;
+}
+
+
+/*!
+ * @brief usbd_device_request_irq() - process a device request
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param bus
+ * @param request
+ * @return non-zero if errror
+ */
+int usbd_device_request_irq(struct usbd_bus_instance *bus, struct usbd_device_request *request)
+{
+ struct usbd_function_instance *function = bus->function_instance;
+ u8 bRequest = request->bRequest;
+ u8 bmRequestType = request->bmRequestType;
+
+ /* Handle only USB Standard Requests (c.f. USB Spec table 9-2, D6..5 must be 0),
+ * otherwise call the currently enabled function specific receive setup.
+ */
+ TRACE_SETUP(USBD, request);;
+ if (!(bmRequestType & USB_REQ_TYPE_MASK) &&
+ ((bmRequestType & USB_DIR_IN ? d2h_standard_requests : h2d_standard_requests)
+ [bmRequestType & 0x3] & STD(bRequest)))
+ return do_standard_device_request(function, request);
+ else
+ return do_non_standard_device_request(function, request);
+
+}
+OTG_EXPORT_SYMBOL(usbd_device_request_irq);
+OTG_EXPORT_SYMBOL(usbd_get_descriptor);
+
+/* ************************************************************************** */
+/*!
+ * Device I/O:
+ * usbd_urb_finished()
+ * usbd_first_urb_detached()
+ * usbd_find_endpoint_address()
+ */
+
+/*!
+ * @brief usbd_do_urb_callback() - tell function that an urb has been transmitted.
+ *
+ * Must be called from an interrupt or with interrupts disabled.
+ *
+ * Used by a USB Bus driver to pass a sent urb back to the function
+ * driver via the endpoints done queue.
+ *
+ * If there is no callback or if the URB call back returns non-zero then the
+ * urb must be de-allocated here.
+ *
+ * @param urb
+ * @param rc
+ */
+void usbd_do_urb_callback (struct usbd_urb *urb, int rc)
+{
+ TRACE_MSG1(USBD,"urb: %p", urb);
+ RETURN_UNLESS (urb);
+ if (!urb->notify || urb->notify(urb,rc))
+ usbd_free_urb(urb);
+}
+
+/*!
+ * @brief usbd_find_endpoint_index()
+
+ * Find the endpoint map index for the specified bEndpointAddress.
+
+ * @param bus
+ * @param bEndpointAddress
+ */
+int usbd_find_endpoint_index(struct usbd_bus_instance *bus, int bEndpointAddress)
+{
+ int i,rc1;
+ rc1=0;
+ for (i = 0; i < bus->endpoints; i++){
+ rc1=i;
+ BREAK_IF (bus->endpoint_array[i].bEndpointAddress[bus->high_speed] == bEndpointAddress);
+ }
+ return rc1;
+}
+
+OTG_EXPORT_SYMBOL(usbd_find_endpoint_index);
+OTG_EXPORT_SYMBOL(usbd_do_urb_callback);
+
+
+/* ************************************************************************** */
+/* Device init ************************************************************** */
+
+extern char * usbd_function_name(int n);
+struct usbd_ops usbd_ops;
+otg_tag_t USBD;
+
+/*!
+ * @brief usbd_device_init() - initialize
+ */
+int usbd_device_init (void)
+{
+ //USBD = otg_trace_obtain_tag(NULL, "usbd");
+ TRACE_MSG0(USBD,"--");
+ //otg_sem_init_unlocked("usbd_device", &usbd_bus_sem);
+ usbd_ops.function_name = usbd_function_name;
+ otg_set_usbd_ops(&usbd_ops);
+ return 0;
+}
+
+/*!
+ * @brief usbd_device_exit() - de-initialize
+ */
+void usbd_device_exit (void)
+{
+ otg_set_usbd_ops(NULL);
+ //otg_trace_invalidate_tag(USBD);
+}
+
+/* Module init ************************************************************** */
+/*!
+ * @brief usbd_device_init() - initialize
+ */
+int usbd_device_modinit (void)
+{
+ USBD = otg_trace_obtain_tag(NULL, "usbd");
+ otg_sem_init_unlocked("usbd_bus", &usbd_bus_sem);
+ return 0;
+}
+
+/*!
+ * @brief usbd_device_exit() - de-initialize
+ */
+void usbd_device_modexit (void)
+{
+ otg_trace_invalidate_tag(USBD);
+}
diff --git a/drivers/otg/otgcore/usbp-fops.c b/drivers/otg/otgcore/usbp-fops.c
new file mode 100644
index 000000000000..49ac62624882
--- /dev/null
+++ b/drivers/otg/otgcore/usbp-fops.c
@@ -0,0 +1,2385 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/usbp-fops.c - USB Function support
+ * @(#) sl@belcarra.com/whiskey.enposte.net|otg/otgcore/usbp-fops.c|20070816060612|51897
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+
+/*!
+ * @file otg/otgcore/usbp-fops.c
+ * @brief Function driver related functions.
+ *
+ * This implements the functions used to implement USB Function drivers.
+ *
+ * @ingroup USBDCORE
+ */
+
+#include <otg/otg-compat.h>
+#include <otg/otg-module.h>
+
+#include <otg/usbp-chap9.h>
+#include <otg/usbp-cdc.h>
+#include <otg/otg-trace.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+
+#define TRACE_VERBOSE 1
+
+void urb_append(urb_link *hd, struct usbd_urb *urb);
+struct usbd_function_driver *
+usbd_find_function_internal(struct otg_list_node *usbd_function_drivers, const char *name);
+
+//extern struct semaphore usbd_bus_sem;
+extern otg_sem_t usbd_bus_sem;
+
+int usbd_strings_init(struct usbd_bus_instance *bus, struct usbd_function_instance *function_instance);
+void usbd_strings_exit(struct usbd_bus_instance *bus);
+void usbd_free_string_descriptor (struct usbd_bus_instance *bus, u8 index);
+extern void otg_write_info_message(void *, char *);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Simple Function Registration:
+ * usbd_register_simple_function()
+ * usbd_deregister_simple_function()
+ *
+ */
+
+LIST_NODE_INIT(usbd_simple_drivers); // list of all registered configuration function modules
+
+/*!
+ * @brief usbd_register_simple_function() - register a usbd function driver
+ *
+ * USBD Simple Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param simple_driver - driver to register
+ * @param name - driver name
+ * @param privdata - driver private data
+ * @return 0 on success, otherwise NULL
+ *
+ */
+int
+usbd_register_simple_function (struct usbd_simple_driver *simple_driver, const char * name, void *privdata)
+{
+ //TRACE_MSG1(USBD, "SIMPLE function: %s", name);
+ RETURN_EINVAL_IF(otg_sem_wait(&usbd_bus_sem));
+ simple_driver->driver.name = name;
+ simple_driver->driver.privdata = privdata;
+ simple_driver->driver.function_type = function_simple;
+ LIST_ADD_TAIL (&simple_driver->driver.drivers, &usbd_simple_drivers);
+ simple_driver->driver.flags |= FUNCTION_REGISTERED;
+ otg_sem_post(&usbd_bus_sem);
+ return 0;
+}
+
+/*!
+ * @brief usbd_deregister_simple_function() - de-register a usbd function driver instance
+ *
+ * USBD Function drivers call this to de-register with the USBD Core layer.
+ * @param simple_driver - driver to de-register
+ */
+void usbd_deregister_simple_function (struct usbd_simple_driver *simple_driver)
+{
+ RETURN_UNLESS (simple_driver);
+ TRACE_MSG1(USBD, "SIMPLE function: %s", simple_driver->driver.name);
+ while (otg_sem_wait(&usbd_bus_sem));
+ if (simple_driver->driver.flags & FUNCTION_ENABLED) {
+ // XXX ERROR need to disable
+ }
+ if (simple_driver->driver.flags & FUNCTION_REGISTERED) {
+ simple_driver->driver.flags &= ~FUNCTION_REGISTERED;
+ LIST_DEL (simple_driver->driver.drivers);
+ }
+ otg_sem_post(&usbd_bus_sem);
+}
+
+/*!
+ * @brief usbp_find_simple_driver() - register a usbd function driver
+ *
+ * USBD Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param name - by which to look for driver
+ * @return function instance
+ *
+ */
+struct usbd_simple_driver *
+usbp_find_simple_driver (char *name)
+{
+ return (struct usbd_simple_driver *) usbd_find_function_internal(&usbd_simple_drivers, name);
+}
+
+OTG_EXPORT_SYMBOL(usbd_register_simple_function);
+OTG_EXPORT_SYMBOL(usbd_deregister_simple_function);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Function Registration:
+ * usbd_register_class_function_function()
+ * usbd_register_interface_function()
+ * usbd_register_composite_function()
+ * usbd_deregister_class_function()
+ * usbd_deregister_interface_function()
+ * usbd_deregister_composite_function()
+ */
+
+LIST_NODE_INIT(usbd_interface_drivers); // list of all registered interface function modules
+LIST_NODE_INIT(usbd_class_drivers); // list of all registered composite function modules
+LIST_NODE_INIT(usbd_composite_drivers); // list of all registered composite function modules
+
+/*!
+ * @brief usbd_register_interface_function() - register a usbd function driver
+ *
+ * USBD Interface Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param interface_driver - driver to register
+ * @param name - driver name
+ * @param privdata - driver's private data
+ * @return 0 on finish, or NULL
+ *
+ */
+int
+usbd_register_interface_function(struct usbd_interface_driver *interface_driver, const char * name, void *privdata)
+{
+ //TRACE_MSG1(USBD, "INTERFACE function: %s", name);
+ RETURN_EINVAL_IF(otg_sem_wait(&usbd_bus_sem));
+ interface_driver->driver.name = name;
+ interface_driver->driver.privdata = privdata;
+ interface_driver->driver.function_type = function_interface;
+ LIST_ADD_TAIL (&interface_driver->driver.drivers, &usbd_interface_drivers);
+ interface_driver->driver.flags |= FUNCTION_REGISTERED;
+ otg_sem_post(&usbd_bus_sem);
+ return 0;
+}
+
+/*!
+ * @brief usbd_register_class_function() - register a usbd function driver
+ *
+ * USBD composite Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param class_driver- class driver to register
+ * @param name - class driver name
+ * @param privdata -class driver private data
+ * @return 0 on finish, NULL on failure
+ *
+ */
+int
+usbd_register_class_function(struct usbd_class_driver *class_driver, const char * name, void *privdata)
+{
+ //TRACE_MSG1(USBD, "CLASS function: %s", name);
+ RETURN_EINVAL_IF(otg_sem_wait(&usbd_bus_sem));
+ class_driver->driver.name = name;
+ class_driver->driver.privdata = privdata;
+ class_driver->driver.function_type = function_class;
+ LIST_ADD_TAIL (&class_driver->driver.drivers, &usbd_class_drivers);
+ class_driver->driver.flags |= FUNCTION_REGISTERED;
+ otg_sem_post(&usbd_bus_sem);
+ return 0;
+}
+
+/*!
+ * usbd_register_composite_function() - register a usbd function driver
+ *
+ * USBD composite Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param composite_driver - function driver to register
+ * @param name - function driver's register name
+ * @param class_name - function driver's class name
+ * @param interface_function_names
+ * @param privdata - function driver's private data
+ * @return 0 on success, or NULL
+ *
+ */
+int
+usbd_register_composite_function (struct usbd_composite_driver *composite_driver, const char* name,
+ const char* class_name, const char **interface_function_names, void *privdata)
+{
+ TRACE_MSG1(USBD, "COMPOSITE function: %s", name);
+ RETURN_EINVAL_IF(otg_sem_wait(&usbd_bus_sem));
+ composite_driver->driver.name = name;
+ composite_driver->driver.privdata = privdata;
+ composite_driver->driver.function_type = function_composite;
+ composite_driver->class_name = class_name;
+ composite_driver->interface_function_names = interface_function_names;
+ LIST_ADD_TAIL (&composite_driver->driver.drivers, &usbd_composite_drivers);
+ composite_driver->driver.flags |= FUNCTION_REGISTERED;
+ otg_sem_post(&usbd_bus_sem);
+ return 0;
+}
+
+/*!
+ * usbd_deregister_interface_function() - de-register a usbd function driver instance
+ *
+ * USBD Function drivers call this to de-register with the USBD Core layer.
+ * @param interface_driver - driver to de-register
+ */
+void usbd_deregister_interface_function (struct usbd_interface_driver *interface_driver)
+{
+ RETURN_UNLESS (interface_driver);
+ //TRACE_MSG1(USBD, "INTERFACE function: %s", interface_driver->driver.name);
+ while (otg_sem_wait(&usbd_bus_sem));
+ if (interface_driver->driver.flags & FUNCTION_ENABLED) {
+ // XXX ERROR need to disable
+ }
+ if (interface_driver->driver.flags & FUNCTION_REGISTERED) {
+ interface_driver->driver.flags &= ~FUNCTION_REGISTERED;
+ LIST_DEL (interface_driver->driver.drivers);
+ }
+ otg_sem_post(&usbd_bus_sem);
+}
+
+/*!
+ * @brief usbd_deregister_class_function() - de-register a usbd function driver instance
+ *
+ * USBD Function drivers call this to de-register with the USBD Core layer.
+ * @param class_driver - pointer to class driver to de-register
+ */
+void usbd_deregister_class_function (struct usbd_class_driver *class_driver)
+{
+ RETURN_UNLESS (class_driver);
+ //TRACE_MSG1(USBD, "CLASS function: %s", class_driver->driver.name);
+ while (otg_sem_wait(&usbd_bus_sem));
+ if (class_driver->driver.flags & FUNCTION_ENABLED) {
+ // XXX ERROR need to disable
+ }
+ if (class_driver->driver.flags & FUNCTION_REGISTERED) {
+ class_driver->driver.flags &= ~FUNCTION_REGISTERED;
+ LIST_DEL (class_driver->driver.drivers);
+ }
+ otg_sem_post(&usbd_bus_sem);
+}
+
+/*!
+ * @brief usbd_deregister_composite_function() - de-register a usbd function driver instance
+ *
+ * USBD Function drivers call this to de-register with the USBD Core layer.
+ * @param composite_driver
+ */
+void usbd_deregister_composite_function (struct usbd_composite_driver *composite_driver)
+{
+ RETURN_UNLESS (composite_driver);
+ //TRACE_MSG1(USBD, "COMPOSITE function: %s", composite_driver->driver.name);
+ while (otg_sem_wait(&usbd_bus_sem));
+ if (composite_driver->driver.flags & FUNCTION_ENABLED) {
+ // XXX ERROR need to disable
+ }
+ if (composite_driver->driver.flags & FUNCTION_REGISTERED) {
+ composite_driver->driver.flags &= ~FUNCTION_REGISTERED;
+ LIST_DEL (composite_driver->driver.drivers);
+ }
+ otg_sem_post(&usbd_bus_sem);
+}
+
+/*!
+ * @brief usbp_find_interface_driver() - register a usbd function driver
+ *
+ * USBD Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param name - by which to search
+ * @return found interface driver
+ *
+ */
+struct usbd_interface_driver *usbp_find_interface_driver(const char *name)
+{
+ #if defined(CONFIG_OTG_TRACE)
+ TRACE_STRING(USBD, "name: %s", (char *)name);
+ #endif
+
+ return (struct usbd_interface_driver *) usbd_find_function_internal(&usbd_interface_drivers, name);
+}
+
+
+/*!
+ * @brief usbp_find_class_driver() - register a usbd function driver
+ *
+ * USBD Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param name - driver's name by which to search
+ * @return function instance
+ *
+ */
+struct usbd_class_driver *usbp_find_class_driver(const char *name)
+{
+ TRACE_MSG1(USBD, "%s", name);
+ return (struct usbd_class_driver *) usbd_find_function_internal(&usbd_class_drivers, name);
+}
+
+/*!
+ * @brief usbp_find_composite_driver() - register a usbd function driver
+ *
+ * USBD Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * This will typically only be called within usbd_bus_sem protected area.
+ *
+ * @param name - by which to search
+ * @return usbd_composite_driver instance
+ *
+ */
+struct usbd_composite_driver *usbp_find_composite_driver(char *name)
+{
+ TRACE_MSG1(USBD, "%s", name);
+ return (struct usbd_composite_driver *) usbd_find_function_internal(&usbd_composite_drivers, name);
+}
+
+
+OTG_EXPORT_SYMBOL(usbd_register_interface_function);
+OTG_EXPORT_SYMBOL(usbd_register_composite_function);
+OTG_EXPORT_SYMBOL(usbd_register_class_function);
+OTG_EXPORT_SYMBOL(usbd_deregister_interface_function);
+OTG_EXPORT_SYMBOL(usbd_deregister_class_function);
+OTG_EXPORT_SYMBOL(usbd_deregister_composite_function);
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * @brief copy_descriptor() - copy data into buffer
+ *
+ * @param cp - buffer to receive
+ * @param data - source to copy
+ * @return bLength - copied data length
+ */
+static int copy_descriptor (u8 *cp, void *data)
+{
+ RETURN_ZERO_UNLESS (data);
+ memcpy (cp, data, ((struct usbd_generic_descriptor *)data)->bLength);
+ return ((struct usbd_generic_descriptor *)data)->bLength;
+}
+
+
+/*!
+ * @brief usbp_alloc_simple_configuration_descriptor() -
+ *
+ * @param simple_instance
+ * @param cfg_size
+ * @param hs
+ * @return pointer to configuration descriptor
+ */
+void usbp_alloc_simple_configuration_descriptor(struct usbd_simple_instance *simple_instance, int cfg_size, int hs)
+{
+ struct usbd_simple_driver *simple_driver = (struct usbd_simple_driver *)
+ simple_instance->function.function_driver;
+ struct usbd_bus_instance *bus = simple_instance->function.bus;
+
+
+ struct usbd_configuration_description *configuration_description;
+ struct usbd_configuration_descriptor *configuration_descriptor = NULL;
+
+
+ //int cfg_size = sizeof(struct usbd_configuration_descriptor) + sizeof(struct usbd_otg_descriptor);
+ u8 *cp = NULL;
+
+ //int i, j, k, l;
+ int i, k, l;
+
+ //RETURN_NULL_UNLESS((configuration_descriptor = CKMALLOC(cfg_size)));
+
+ configuration_description = simple_driver->configuration_description;
+
+ configuration_descriptor = simple_instance->configuration_descriptor[hs];
+
+ cfg_size = sizeof(struct usbd_configuration_descriptor) + sizeof(struct usbd_otg_descriptor);
+
+ /* Compose the configuration descriptor
+ * First copy the function driver configuration configuration descriptor.
+ */
+ cp = (u8 *)configuration_descriptor;
+ //cfg_size = copy_descriptor(cp + 0, configuration_description->configuration_descriptor);
+ cfg_size = sizeof(struct usbd_configuration_descriptor);
+ configuration_descriptor->bLength = sizeof(struct usbd_configuration_descriptor);
+ configuration_descriptor->bDescriptorType = USB_DT_CONFIGURATION;
+
+ /* copy peripheral controller / platform dependant values into configuration */
+ configuration_descriptor->bmAttributes = USB_BMATTRIBUTE_RESERVED |
+ (bus->bmAttributes & (USB_BMATTRIBUTE_SELF_POWERED | USB_BMATTRIBUTE_REMOTE_WAKEUP));
+
+ /* Self Powered devices must set bMaxPower to 1 unit */
+ configuration_descriptor->bMaxPower =
+ ((bus->bmAttributes & USB_BMATTRIBUTE_SELF_POWERED) || !bus->bMaxPower) ? 1: bus->bMaxPower;
+
+ configuration_descriptor->iConfiguration = usbd_alloc_string (&simple_instance->function,
+ configuration_description->iConfiguration);
+
+
+ for (i = 0; i < simple_driver->interfaces; i++) {
+
+ struct usbd_interface_description *interface_description = simple_driver->interface_list + i;
+
+ TRACE_MSG3(USBD, "INTERFACE[%02d] interface_description: %x alternates: %d", i,
+ interface_description, interface_description->alternates);
+
+ for (k = 0; k < interface_description->alternates; k++) {
+
+ struct usbd_alternate_description *alternate_description = interface_description->alternate_list + k;
+
+ struct usbd_interface_descriptor *interface_descriptor;
+
+ TRACE_MSG2(USBD, "ALTERNATE[%02d:%02d]", i, k);
+
+ /* copy alternate interface descriptor, update descriptor fields
+ */
+ interface_descriptor = (struct usbd_interface_descriptor *)(cp + cfg_size);
+
+ //cfg_size += copy_descriptor(cp + cfg_size, alternate_description->interface_descriptor);
+ cfg_size += sizeof(struct usbd_interface_descriptor);
+
+ interface_descriptor->bLength = sizeof(struct usbd_interface_descriptor);
+ interface_descriptor->bDescriptorType = USB_DT_INTERFACE;
+ interface_descriptor->bInterfaceNumber = i;
+ interface_descriptor->bAlternateSetting = k;
+ interface_descriptor->bNumEndpoints = alternate_description->endpoints;
+
+ interface_descriptor->bInterfaceClass = alternate_description->bInterfaceClass;
+ interface_descriptor->bInterfaceSubClass = alternate_description->bInterfaceSubClass;
+ interface_descriptor->bInterfaceProtocol = alternate_description->bInterfaceProtocol;
+ // XXX
+ interface_descriptor->iInterface = usbd_alloc_string (&simple_instance->function,
+ alternate_description->iInterface);
+
+ TRACE_MSG6(USBD, "COPY ALT[%02d:%02d] classes: %d endpoints: %d %s size: %d",
+ i, k, alternate_description->classes, alternate_description->endpoints,
+ alternate_description->iInterface, cfg_size);
+
+ /* copy class descriptors
+ */
+ for (l = 0; l < alternate_description->classes; l++)
+ cfg_size += copy_descriptor(cp + cfg_size, *(alternate_description->class_list + l));
+
+ /* copy endpoint descriptors, update descriptor fields
+ */
+ for (l = 0; l < alternate_description->endpoints; l++) {
+ int index = alternate_description->endpoint_index[l];
+ struct usbd_endpoint_map *endpoint_map = simple_instance->endpoint_map_array + index;
+ struct usbd_endpoint_descriptor *endpoint_descriptor =
+ (struct usbd_endpoint_descriptor *)(cp + cfg_size);
+
+ cfg_size += sizeof(struct usbd_endpoint_descriptor);
+ endpoint_descriptor->bLength = sizeof(struct usbd_endpoint_descriptor);;
+ endpoint_descriptor->bDescriptorType = USB_DT_ENDPOINT;
+ endpoint_descriptor->bEndpointAddress = endpoint_map->bEndpointAddress[hs];
+ endpoint_descriptor->bmAttributes = endpoint_map->bmAttributes[hs] & 0x3;
+ endpoint_descriptor->wMaxPacketSize = cpu_to_le16(endpoint_map->wMaxPacketSize[hs]);
+ endpoint_descriptor->bInterval = endpoint_map->bInterval[hs];
+ }
+ }
+ }
+ TRACE_MSG2(USBD, "cfg: %x size: %d", cp, cfg_size);
+
+ configuration_descriptor->wTotalLength = cpu_to_le16(cfg_size);
+ configuration_descriptor->bNumInterfaces = simple_driver->interfaces;
+ configuration_descriptor->bConfigurationValue = 1;
+
+ #if 0
+ for (i = 0; i < cfg_size; i+= 8) {
+ TRACE_MSG8(USBD, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+ #endif
+}
+
+/*!
+ * @brief usbp_dealloc_simple_instance() -
+ *
+ * @param simple_instance
+ */
+void usbp_dealloc_simple_instance(struct usbd_simple_instance *simple_instance)
+{
+ int i;
+
+ usbd_strings_exit(simple_instance->function.bus);
+
+ for (i = 0; i < 2; i++)
+ if (simple_instance->configuration_descriptor[i])
+ LKFREE(simple_instance->configuration_descriptor[i]);
+
+ if (simple_instance->endpoint_map_array) LKFREE(simple_instance->endpoint_map_array);
+
+ //if (simple_instance->requestedEndpoints) LKFREE(simple_instance->requestedEndpoints);
+ //if (simple_instance->interface_list) LKFREE(simple_instance->interface_list);
+
+ LKFREE(simple_instance);
+}
+
+/*!
+ * @brief usbp_alloc_simple_instance() - create a usbp_simple_instance
+ *
+ * @param simple_driver - usbd_simple_driver instance pointer
+ * @param bus - usbd_bus_instance pointer
+ * @return usbd_simple_instance
+ */
+struct usbd_simple_instance *
+usbp_alloc_simple_instance(struct usbd_simple_driver *simple_driver,
+ struct usbd_bus_instance *bus)
+{
+ struct usbd_simple_instance *simple_instance = NULL;
+ int i, k, l;
+ int cfg_size = sizeof(struct usbd_configuration_descriptor) + sizeof(struct usbd_otg_descriptor);
+ struct usbd_configuration_descriptor *configuration_descriptor[2] = { NULL, NULL, };
+ struct usbd_endpoint_map *endpoint_map_array = NULL;
+ u8 *altsettings = NULL;
+
+ THROW_UNLESS((simple_instance = CKMALLOC(sizeof(struct usbd_simple_instance))), error);
+ simple_instance->function.bus = bus;
+ THROW_IF(usbd_strings_init(bus, &simple_instance->function), error);
+
+ /* copy fields
+ */
+ simple_instance->function.function_driver = (struct usbd_function_driver *)simple_driver;
+ simple_instance->function.function_type = function_simple;
+ simple_instance->function.name = simple_driver->driver.name;
+ simple_instance->function.bus = bus;
+
+ /*
+ * Find and total descriptor sizes.
+ */
+ for (i = 0; i < simple_driver->interfaces; i++) {
+
+ struct usbd_interface_description *interface_description = simple_driver->interface_list + i;
+
+ for (k = 0; k < interface_description->alternates; k++) {
+
+ struct usbd_alternate_description *alternate_description =
+ interface_description->alternate_list + k;
+
+ /* size interface descriptor
+ */
+ cfg_size += sizeof(struct usbd_interface_descriptor);;
+
+ TRACE_MSG6(USBD, "SIZE ALT[%02d:%02d] classes: %d endpoints: %d %s size: %d",
+ i, k, alternate_description->classes,
+ alternate_description->endpoints, alternate_description->iInterface, cfg_size);
+
+ /* size class descriptors
+ */
+ for (l = 0; l < alternate_description->classes; l++) {
+ struct usbd_generic_class_descriptor *class_descriptor =
+ *(alternate_description->class_list + l);
+ cfg_size += class_descriptor->bLength;
+ }
+
+ cfg_size += alternate_description->endpoints * sizeof(struct usbd_endpoint_descriptor);
+
+ #if 0
+ /* size endpoint descriptors (in theory could be endpoints * sizeof())
+ */
+ for (l = 0; l < alternate_description->endpoints; l++) {
+ struct usbd_endpoint_descriptor *endpoint_descriptor =
+ *(alternate_description->endpoint_list + l);
+ cfg_size += endpoint_descriptor->bLength;
+ }
+ #endif
+
+ }
+ }
+ TRACE_MSG1(USBD, "cfg_size: %d", cfg_size);
+
+ /* allocate configuration descriptor
+ */
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ for (i = 0; i < 2; i++)
+ #else /* CONFIG_OTG_HIGH_SPEED */
+ for (i = 0; i < 1; i++)
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+ {
+ THROW_UNLESS((configuration_descriptor[i] = (struct usbd_configuration_descriptor *)
+ CKMALLOC (cfg_size + 4)), error);
+ simple_instance->configuration_descriptor[i] = configuration_descriptor[i];
+ }
+ simple_instance->configuration_size = cfg_size;
+
+ /* allocate endpoint map array and altsetting
+ */
+ THROW_UNLESS((endpoint_map_array = (struct usbd_endpoint_map *) CKMALLOC (sizeof(struct usbd_endpoint_map) *
+ simple_driver->endpointsRequested)), error);
+ simple_instance->endpoint_map_array = endpoint_map_array;
+
+ THROW_UNLESS((altsettings = (u8 *)CKMALLOC(sizeof (u8) * simple_instance->interfaces)), error);
+ simple_instance->altsettings = altsettings;
+
+ TRACE_MSG1(USBD, "request endpoints: %x", simple_driver->endpointsRequested);
+
+ /* request endpoints, allocate configuration descriptor, updating endpoint descriptors with correct
+ * information from endpoint map allocated above and set the endpoints
+ */
+
+ THROW_IF(bus->driver->bops->set_endpoints( bus, simple_driver->endpointsRequested,
+ simple_instance->endpoint_map_array), error);
+
+ return simple_instance;
+
+ CATCH(error) {
+ TRACE_MSG0(USBD, "FAILED");
+ for (i = 0; i < 2; i++) if (configuration_descriptor[i]) LKFREE(configuration_descriptor[i]);
+ if (altsettings) LKFREE(altsettings);
+ if (endpoint_map_array) LKFREE(endpoint_map_array);
+ if (simple_instance) {
+ usbd_strings_exit(bus);
+ usbp_dealloc_simple_instance(simple_instance);
+ }
+ return NULL;
+ }
+}
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * @brief usbp_dealloc_composite_instance() - free composite instance and associated resources
+ *
+ * @param composite_instance
+ */
+void usbp_dealloc_composite_instance(struct usbd_composite_instance *composite_instance)
+{
+ int i;
+ for (i = 0; i < 2; i++)
+ if (composite_instance->configuration_descriptor[i])
+ LKFREE(composite_instance->configuration_descriptor[i]);
+
+ usbd_strings_exit(composite_instance->function.bus);
+ if (composite_instance->interfaces_array) LKFREE(composite_instance->interfaces_array);
+ if (composite_instance->endpoint_map_array) LKFREE(composite_instance->endpoint_map_array);
+ if (composite_instance->requestedEndpoints) LKFREE(composite_instance->requestedEndpoints);
+ if (composite_instance->interface_list) LKFREE(composite_instance->interface_list);
+
+ LKFREE(composite_instance);
+}
+
+
+/*!
+ * @brief usbp_alloc_composite_configuration_descriptor() -
+ *
+ * @param composite_instance
+ * @param cfg_size
+ * @param hs
+ * @return pointer to configuration descriptor
+ */
+void usbp_alloc_composite_configuration_descriptor(struct usbd_composite_instance *composite_instance, int cfg_size, int hs)
+{
+ struct usbd_composite_driver *composite_driver = (struct usbd_composite_driver*)
+ composite_instance->function.function_driver;
+ struct usbd_bus_instance *bus = composite_instance->function.bus;
+ struct usbd_configuration_description *configuration_description = composite_driver->configuration_description;
+
+ struct usbd_configuration_descriptor *configuration_descriptor = composite_instance->configuration_descriptor[hs];
+ u8 *cp = NULL;
+
+ int i, j, k, l, m;
+
+ /* Compose the configuration descriptor
+ * First copy the function driver configuration configuration descriptor.
+ */
+ cp = (u8 *)configuration_descriptor;
+
+ TRACE_MSG4(USBD, "bDeviceClass: %02x bus->bmAttributes: %02x descriptor[%d] %x",
+ composite_driver->device_description->bDeviceClass,
+ bus->bmAttributes,
+ hs, composite_instance->configuration_descriptor[hs]);
+
+ //cfg_size = copy_descriptor(cp + 0, configuration_description->configuration_descriptor);
+ cfg_size = sizeof(struct usbd_configuration_descriptor);
+ configuration_descriptor->bLength = sizeof(struct usbd_configuration_descriptor);
+
+ /* copy peripheral controller / platform dependant values into configuration */
+ configuration_descriptor->bDescriptorType = USB_DT_CONFIGURATION;
+
+ /* Self Powered devices must set bMaxPower to 1 unit */
+ configuration_descriptor->bmAttributes = USB_BMATTRIBUTE_RESERVED |
+ (bus->bmAttributes & (USB_BMATTRIBUTE_SELF_POWERED | USB_BMATTRIBUTE_REMOTE_WAKEUP));
+
+ /* Self Powered devices must set bMaxPower to 1 unit */
+ configuration_descriptor->bMaxPower =
+ ((bus->bmAttributes & USB_BMATTRIBUTE_SELF_POWERED) || !bus->bMaxPower) ? 1: bus->bMaxPower;
+
+ configuration_descriptor->iConfiguration = usbd_alloc_string (&composite_instance->function,
+ configuration_description->iConfiguration);
+
+ for (i = 0; i < composite_instance->interface_functions; i++) {
+
+ struct usbd_interface_instance *interface_instance = composite_instance->interfaces_array +i;
+ struct usbd_interface_driver *interface_driver = (struct usbd_interface_driver *)
+ interface_instance->function.function_driver;
+
+ //TRACE_MSG6(USBD, "INTERFACE[%02d] interfaces: %d endpoints: %d bFunctionClass: %d hs: %d %s", i,
+ // interface_driver->interfaces, interface_driver->endpointsRequested,
+ // interface_driver->bFunctionClass, hs,
+ // interface_driver->driver.name);
+
+ TRACE_MSG8(USBD, "INTERFACE[%02d:%02d] %x endpoints: %d endpoint_map_array: %x "
+ "bFunctionClass: %d hs: %d %s",
+ i, interface_driver->interfaces,
+ interface_instance,
+ interface_driver->endpointsRequested,
+ interface_instance->endpoint_map_array,
+ interface_driver->bFunctionClass,
+ hs,
+ interface_driver->driver.name);
+
+ /* insert Interface Association Descriptor if required
+ */
+ if ((USB_CLASS_MISC == composite_driver->device_description->bDeviceClass)) {
+
+ struct usbd_interface_association_descriptor *iad =
+ (struct usbd_interface_association_descriptor *) (cp + cfg_size);
+
+ TRACE_MSG4(USBD, " IAD[%02d] Class: %d SubClass: %d Protocol: %d", i,
+ interface_driver->bFunctionClass, interface_driver->bFunctionSubClass,
+ interface_driver->bFunctionProtocol);
+
+ cfg_size += sizeof(struct usbd_interface_association_descriptor);
+ iad->bLength = sizeof(struct usbd_interface_association_descriptor);
+ iad->bDescriptorType = USB_DT_INTERFACE_ASSOCIATION;
+ iad->bFirstInterface = interface_instance->wIndex;
+ iad->bInterfaceCount = interface_driver->interfaces;
+ iad->bFunctionClass = interface_driver->bFunctionClass;
+ iad->bFunctionSubClass = interface_driver->bFunctionSubClass;
+ iad->bFunctionProtocol = interface_driver->bFunctionProtocol;
+ iad->iFunction = usbd_alloc_string (&composite_instance->function,
+ interface_driver->iFunction);
+ }
+
+ /* iterate across interfaces and alternates for this for this function
+ * XXX this will need to be modified if interface functions are allowed
+ * to define multiple interfaces.
+ */
+
+ for (j = 0; j < interface_driver->interfaces; j++) {
+
+ struct usbd_interface_description *interface_description = interface_driver->interface_list + j;
+
+ TRACE_MSG5(USBD, "INTERFACE[%02d:%02d] interface_description: %x alternates: %d wIndex: %d", i, j,
+ interface_description, interface_description->alternates,
+ interface_instance->wIndex);
+
+ for (k = 0; k < interface_description->alternates; k++) {
+
+ struct usbd_alternate_description *alternate_description =
+ interface_description->alternate_list + k;
+
+ struct usbd_interface_descriptor *interface_descriptor;
+
+ TRACE_MSG3(USBD, "ALTERNATE[%02d:%02d:%02d]", i, j, k);
+
+ /* copy alternate interface descriptor, update descriptor fields
+ */
+ interface_descriptor = (struct usbd_interface_descriptor *)(cp + cfg_size);
+
+ //cfg_size += copy_descriptor(cp + cfg_size, alternate_description->interface_descriptor);
+ cfg_size += sizeof(struct usbd_interface_descriptor);
+
+ //interface_descriptor->bInterfaceNumber = i; // XXX see note above
+ interface_descriptor->bLength = sizeof(struct usbd_interface_descriptor);
+ interface_descriptor->bDescriptorType = USB_DT_INTERFACE;
+ interface_descriptor->bInterfaceNumber = interface_instance->wIndex + j;
+ interface_descriptor->bAlternateSetting = k;
+ interface_descriptor->bNumEndpoints = alternate_description->endpoints;
+
+ interface_descriptor->bInterfaceClass = alternate_description->bInterfaceClass;
+ interface_descriptor->bInterfaceSubClass = alternate_description->bInterfaceSubClass;
+ interface_descriptor->bInterfaceProtocol = alternate_description->bInterfaceProtocol;
+
+ interface_descriptor->iInterface = usbd_alloc_string (&composite_instance->function,
+ alternate_description->iInterface);
+
+ TRACE_MSG7(USBD, "COPY ALT[%02d:%02d:%02d] classes: %d endpoints: %d %s size: %d", i, j, k,
+ alternate_description->classes, alternate_description->endpoints,
+ alternate_description->iInterface, cfg_size);
+
+ /* copy class descriptors
+ */
+ for (l = 0; l < alternate_description->classes; l++) {
+ struct usbd_generic_class_descriptor *generic_descriptor =
+ (struct usbd_generic_class_descriptor *)(cp + cfg_size);
+
+ cfg_size += copy_descriptor(cp + cfg_size, *(alternate_description->class_list + l));
+
+ TRACE_MSG7(USBD, "COPY CLS[%02d:%02d:%02d:%02d] type: %02x:%02x:%02x",
+ i, j, k, l,
+ generic_descriptor->bLength,
+ generic_descriptor->bDescriptorType,
+ generic_descriptor->bDescriptorSubtype
+ );
+
+ /* update
+ * call management functional descriptor
+ * union functional descriptor
+ * and update interface numbers
+ */
+ CONTINUE_UNLESS (USB_DT_CLASS_SPECIFIC == generic_descriptor->bDescriptorType);
+ switch (generic_descriptor->bDescriptorSubtype) {
+ case USB_ST_CMF: {
+ struct usbd_call_management_functional_descriptor *cmfd =
+ (struct usbd_call_management_functional_descriptor *)
+ generic_descriptor;
+ cmfd->bDataInterface += interface_instance->wIndex + j;
+ TRACE_MSG5(USBD, "COPY CLS[%02d:%02d:%02d:%02d] CMF bDataInterface %02x",
+ i, j, k, l, cmfd->bDataInterface);
+ break;
+ }
+ case USB_ST_UF: {
+ struct usbd_union_functional_descriptor *ufd =
+ (struct usbd_union_functional_descriptor *)
+ generic_descriptor;
+ ufd->bMasterInterface = interface_instance->wIndex + j;
+ TRACE_MSG6(USBD, "COPY CLS[%02d:%02d:%02d:%02d] UFD "
+ "bMasterInterface %02x "
+ "bSlaveInterface %02x",
+ i, j, k, l,
+ ufd->bMasterInterface,
+ ufd->bSlaveInterface[0]
+ );
+
+ for (m = 0; m < interface_driver->interfaces - 1; m++) {
+
+
+ TRACE_MSG7(USBD, "COPY CLS[%02d:%02d:%02d:%02d] UFD "
+ "bSlaveInterface[%02x] %02x ->%02x",
+ i, j, k, l, m,
+ ufd->bSlaveInterface[m],
+ ufd->bSlaveInterface[m] + ufd->bMasterInterface
+ );
+ ufd->bSlaveInterface[m] += ufd->bMasterInterface;
+ }
+ break;
+ }
+ default: break;
+ }
+ }
+
+ /* copy endpoint descriptors, update descriptor fields
+ */
+ for (l = 0; l < alternate_description->endpoints; l++) {
+ int index = alternate_description->endpoint_index[l];
+ struct usbd_endpoint_map *endpoint_map = interface_instance->endpoint_map_array + index;
+ struct usbd_endpoint_descriptor *endpoint_descriptor =
+ (struct usbd_endpoint_descriptor *)(cp + cfg_size);
+
+ TRACE_MSG4(USBD, "REQUEST[%02d:%02d] index: %d endpoint_map: %x",
+ l, alternate_description->endpoints, index, endpoint_map);
+
+ cfg_size += sizeof(struct usbd_endpoint_descriptor);
+ endpoint_descriptor->bLength = sizeof(struct usbd_endpoint_descriptor);;
+ endpoint_descriptor->bDescriptorType = USB_DT_ENDPOINT;
+ endpoint_descriptor->bEndpointAddress = endpoint_map->bEndpointAddress[hs];
+ endpoint_descriptor->bmAttributes = endpoint_map->bmAttributes[hs] & 0x3;
+ endpoint_descriptor->wMaxPacketSize = cpu_to_le16(endpoint_map->wMaxPacketSize[hs]);
+ endpoint_descriptor->bInterval = endpoint_map->bInterval[hs];
+ }
+ }
+ }
+ }
+
+ #if defined(CONFIG_OTG_BDEVICE_WITH_SRP) || defined(CONFIG_OTG_DEVICE)
+ {
+ struct usbd_otg_descriptor *otg_descriptor = (struct usbd_otg_descriptor *)(cp + cfg_size);
+ otg_descriptor->bLength = sizeof(struct usbd_otg_descriptor);
+ otg_descriptor->bDescriptorType = USB_DT_OTG;
+ otg_descriptor->bmAttributes = bus->driver->bmAttributes & (USB_OTG_HNP_SUPPORTED | USB_OTG_SRP_SUPPORTED);
+ cfg_size += sizeof(struct usbd_otg_descriptor);
+ }
+ #endif /* defined(CONFIG_OTG_BDEVICE_WITH_SRP) || defined(CONFIG_OTG_DEVICE) */
+
+ configuration_descriptor->wTotalLength = cpu_to_le16(cfg_size);
+ configuration_descriptor->bNumInterfaces = composite_instance->interfaces;
+ configuration_descriptor->bConfigurationValue = 1;
+
+ TRACE_MSG2(USBD, "cfg: %x size: %d", cp, cfg_size);
+ #if 0
+ for (i = 0; i < cfg_size; i+= 8) {
+ TRACE_MSG8(USBD, "%02x %02x %02x %02x %02x %02x %02x %02x",
+ cp[i + 0], cp[i + 1], cp[i + 2], cp[i + 3],
+ cp[i + 4], cp[i + 5], cp[i + 6], cp[i + 7]
+ );
+ }
+ #endif
+}
+
+/*!
+ * alloc_function_interface_function() -
+ *
+ * @param interface_driver
+ * @return pointer to interface instance
+ */
+struct usbd_interface_instance *
+alloc_function_interface_function(struct usbd_interface_driver *interface_driver)
+{
+ struct usbd_interface_instance *function_instance = NULL;
+ RETURN_NULL_UNLESS((function_instance = CKMALLOC(sizeof(struct usbd_interface_instance))));
+ function_instance->function.function_type = function_interface;
+ function_instance->function.name = interface_driver->driver.name;
+ return function_instance;
+}
+
+
+/*!
+ * alloc_function_class_function() -
+ *
+ * @param class_driver
+ * @return pointer to class instance
+ */
+struct usbd_class_instance *
+alloc_function_class_function(struct usbd_class_driver *class_driver)
+{
+ struct usbd_class_instance *function_instance = NULL;
+ RETURN_NULL_UNLESS((function_instance = CKMALLOC(sizeof(struct usbd_class_instance))));
+ function_instance->function.function_type = function_class;
+ function_instance->function.name = class_driver->driver.name;
+ return function_instance;
+}
+
+/*!
+ * alloc_usb_string
+ */
+void alloc_usb_strings(struct usbd_function_instance *function, u8 *index, const char **str)
+{
+ TRACE_MSG4(USBD, "function: %x index: %x str: %x %d", function, index, str, str ? *str : NULL);
+
+ for ( ; str && *str; str++, index++) {
+
+ TRACE_MSG2(USBD, "str: %s index: %d", *str, *index);
+
+ if (*index)
+ usbd_realloc_string (function, *index, *str);
+ else
+ usbd_alloc_string (function, *str);
+ }
+}
+
+/*!
+ * @brief usbp_alloc_composite_instance() - register a usbd function driver
+ *
+ * USBD Composite Function drivers call this to register with the USBD Core layer.
+ *
+ * This takes a list of interface functions (by name) to use. Each of these
+ * must be found and the interface descriptor and endpoint request information
+ * must be copied.
+ *
+ * @param composite_driver
+ * @param bus
+ *
+ * @return function instance or NULL on failure.
+ *
+ */
+struct usbd_composite_instance *
+usbp_alloc_composite_instance (struct usbd_composite_driver *composite_driver, struct usbd_bus_instance *bus)
+{
+ struct usbd_composite_instance *composite_instance = NULL;
+ //struct usbd_class_instance *class_instnace = NULL;
+ //struct usbd_class_driver *class_driver = NULL;
+ struct usbd_interface_instance *interfaces_array = NULL;
+ struct usbd_endpoint_request *requestedEndpoints = NULL;
+ struct usbd_endpoint_map *endpoint_map_array = NULL;
+ struct usbd_interface_description *interface_list = NULL;
+
+ u32 interfaces_used;
+
+ const char ** interface_function_names = composite_driver->interface_function_names;
+
+ int i = 0, j = 0, k, l;
+ int endpoint_number, interface_number, function_number = 0;
+ //u8 *cp;
+
+ int cfg_size = sizeof(struct usbd_configuration_descriptor) + sizeof(struct usbd_otg_descriptor);
+
+ THROW_UNLESS((composite_instance = CKMALLOC(sizeof(struct usbd_composite_instance))), error);
+ composite_instance->function.bus = bus;
+ THROW_IF(usbd_strings_init(bus, &composite_instance->function), error);
+
+ /* copy fields
+ */
+ composite_instance->function.function_driver = (struct usbd_function_driver *)composite_driver;
+ composite_instance->function.function_type = function_composite;
+ composite_instance->function.name = composite_driver->driver.name;
+ composite_instance->function.bus = bus;
+ alloc_usb_strings((struct usbd_function_instance*)composite_instance, composite_driver->driver.usb_string_indexes,
+ composite_driver->driver.usb_strings);
+
+ /* count and list interfaces, then allocate interfaces_array.
+ */
+ for (endpoint_number = interface_number = function_number = 0;
+ *(interface_function_names + function_number);
+ function_number++)
+ {
+ const char *interface_name = *(interface_function_names + function_number);
+ char *cp;
+ struct usbd_interface_driver *interface_driver;
+ int interfaces;
+
+ if ((cp = strchr(interface_name, '=')))
+ interface_name = cp + 1;
+
+
+ TRACE_MSG1(USBD, "interface_name: \"%s\"", interface_name ? interface_name : "");
+
+ THROW_UNLESS(interface_driver = usbp_find_interface_driver(interface_name), error);
+
+ TRACE_MSG5(USBD, "INTERFACE[%02d] interfaces: %d endpoints: %d bFunctionClass: %d %s",
+ endpoint_number, interface_driver->interfaces,
+ interface_driver->endpointsRequested, interface_driver->bFunctionClass,
+ interface_driver->driver.name);
+
+ interfaces = interface_driver->interfaces;
+
+ TRACE_MSG6(USBD, "FUNCTION[%02d] interfaces: %d:%d endpoints: %d:%d %20s",
+ function_number, interface_driver->interfaces, interface_number,
+ interface_driver->endpointsRequested, endpoint_number, interface_name);
+
+ interface_number += interface_driver->interfaces;
+ endpoint_number += interface_driver->endpointsRequested;
+
+ if ((USB_CLASS_MISC == composite_driver->device_description->bDeviceClass) ) {
+ cfg_size += sizeof(struct usbd_interface_association_descriptor);
+ TRACE_MSG2(USBD, "FUNCTION[%02d] IAD size: %d", function_number, cfg_size);
+ }
+
+ /* iterate across interfaces for this function and their alternates to get descriptors sizes
+ */
+ for (j = 0; j < interface_driver->interfaces; j++) {
+
+ struct usbd_interface_description *interface_description = interface_driver->interface_list + j;
+
+ for (k = 0; k < interface_description->alternates; k++) {
+
+ struct usbd_alternate_description *alternate_description =
+ interface_description->alternate_list + k;
+
+ /* size interface descriptor
+ */
+ cfg_size += sizeof(struct usbd_interface_descriptor);;
+
+ TRACE_MSG7(USBD, "SIZE ALT[%02d:%02d:%02d] classes: %d endpoints: %d %s size: %d",
+ function_number, j, k, alternate_description->classes,
+ alternate_description->endpoints, alternate_description->iInterface, cfg_size);
+
+ /* size class descriptors
+ */
+ for (l = 0; l < alternate_description->classes; l++) {
+ struct usbd_generic_class_descriptor *class_descriptor =
+ *(alternate_description->class_list + l);
+ cfg_size += class_descriptor->bLength;
+ }
+
+ cfg_size += alternate_description->endpoints * sizeof(struct usbd_endpoint_descriptor);
+
+ #if 0
+ /* size endpoint descriptors (in theory could be endpoints * sizeof())
+ */
+ for (l = 0; l < alternate_description->endpoints; l++) {
+ struct usbd_endpoint_descriptor *endpoint_descriptor =
+ *(alternate_description->endpoint_list + l);
+ cfg_size += endpoint_descriptor->bLength;
+ }
+ #endif
+ }
+ }
+ }
+
+ TRACE_MSG2(USBD, "interfaces: %d endpoints: %d", interface_number, endpoint_number);
+
+ /* Allocate and save information.
+ *
+ * Save endpoints, bNumInterfaces and configuration size, Allocate
+ * configuration descriptor, allocate enough memory for all
+ * interface instances plus one extra for class instance if
+ * required, allocate endpoint map array and endpoint requests.
+ */
+ /*
+ THROW_UNLESS((configuration_descriptor =
+ (struct usbd_configuration_descriptor *) CKMALLOC (cfg_size + 4)), error);
+ composite_instance->configuration_descriptor = configuration_descriptor;
+ */
+
+ THROW_UNLESS((interfaces_array = (struct usbd_interface_instance *)
+ CKMALLOC (sizeof(struct usbd_interface_instance) *
+ (function_number + (composite_driver->class_name ? 1 : 0)))), error);
+
+ THROW_UNLESS((endpoint_map_array = (struct usbd_endpoint_map *)
+ CKMALLOC (sizeof(struct usbd_endpoint_map) * endpoint_number)), error);
+
+ THROW_UNLESS((requestedEndpoints = (struct usbd_endpoint_request *)
+ CKMALLOC (sizeof(struct usbd_endpoint_request) * endpoint_number)), error);
+
+ THROW_UNLESS((interface_list = (struct usbd_interface_description *)
+ CKMALLOC (sizeof(struct usbd_interface_description) * interface_number)), error);
+
+
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ for (i = 0; i < 2; i++)
+ #else /* CONFIG_OTG_HIGH_SPEED */
+ for (i = 0; i < 1; i++)
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+ {
+ THROW_UNLESS((composite_instance->configuration_descriptor[i] =
+ (struct usbd_configuration_descriptor *) CKMALLOC (cfg_size + 4)),
+ error);
+ }
+
+ composite_instance->interfaces_array = interfaces_array;
+ composite_instance->endpoint_map_array = endpoint_map_array;
+ composite_instance->requestedEndpoints = requestedEndpoints;
+ composite_instance->interface_list = interface_list;
+
+ composite_instance->configuration_size = cfg_size; // total configuration size
+ composite_instance->interface_functions = function_number; // number of interface function drivers
+ composite_instance->interfaces = interface_number; // number of interfaces
+ composite_instance->endpointsRequested = endpoint_number; // number of endpoints
+ composite_instance->endpoints = endpoint_number;
+
+ /* Setup interface_instance array.
+ *
+ * Find all interfaces and compose the interface list, count the
+ * endpoints and allocte the endpoint request array. Also find and
+ * total descriptor sizes. Compose the endpoint request array and
+ * configuration descriptor, cannot request endpoints yet as the bus
+ * interface driver is not loaded, so we cannot compose
+ * configuration descriptor here.
+ */
+ for (interfaces_used = endpoint_number = interface_number = function_number = 0;
+ function_number < composite_instance->interface_functions;
+ function_number++)
+ {
+ struct usbd_interface_instance *interface_instance = interfaces_array + function_number;
+ const char *interface_name = *(interface_function_names + function_number);
+ struct usbd_interface_driver *interface_driver;
+ const char *cp;
+ int wIndex = 0;
+
+ if ((cp = strchr(interface_name, '='))) {
+ const char *sp = interface_name;
+ interface_name = cp + 1;
+ for (; sp < cp; sp++) {
+ BREAK_UNLESS(isdigit(*sp));
+ wIndex = wIndex * 10 + *sp - '0';
+ }
+ }
+ else {
+ for (wIndex = 0; wIndex < 32; wIndex++) {
+ BREAK_UNLESS(interfaces_used & (1 << wIndex));
+ }
+ THROW_IF(wIndex == 32, error);
+ }
+
+ THROW_UNLESS(interface_driver = usbp_find_interface_driver(interface_name), error);
+
+ interface_instance->endpoint_map_array = composite_instance->endpoint_map_array + endpoint_number;
+ interface_instance->function.function_driver = (struct usbd_function_driver *)interface_driver;
+ interface_instance->function.function_type = function_interface;
+ interface_instance->function.name = interface_driver->driver.name;
+ interface_instance->function.bus = bus;
+ interface_instance->wIndex = wIndex;
+ interface_instance->lIndex = j;
+
+ alloc_usb_strings((struct usbd_function_instance*)interface_instance,
+ interface_driver->driver.usb_string_indexes,
+ interface_driver->driver.usb_strings);
+
+ for (i = 0; i < interface_driver->interfaces; i++) {
+ THROW_IF(interfaces_used & (1 << (wIndex + 1)), error);
+ interfaces_used |= (1 << (wIndex + i));
+ }
+
+ TRACE_MSG6(USBD, "INTERFACE FUNCTION[%02d] %x interfaces: %d endpointsRequested: %d "
+ "wIndex: %d endpoint_map_array: %x",
+ function_number, interface_instance, interface_driver->interfaces,
+ interface_driver->endpointsRequested, wIndex,
+ interface_instance->endpoint_map_array);
+
+ /* copy requested endpoint information
+ */
+ for (k = 0; k < interface_driver->endpointsRequested; k++, endpoint_number++) {
+
+ TRACE_MSG5(USBD, "REQUEST[%2d %d:%d %d:%d]",
+ function_number, j, interface_number, k, endpoint_number);
+
+ TRACE_MSG3(USBD, "%x %x %d", requestedEndpoints + endpoint_number,
+ interface_driver->requestedEndpoints + j,
+ sizeof(struct usbd_endpoint_request));
+
+ TRACE_MSG5(USBD, "REQUEST[%2d:%2d:%2d] bm: %02x addr: %02x", function_number,
+ k, endpoint_number,
+ interface_driver->requestedEndpoints[k].bmAttributes,
+ interface_driver->requestedEndpoints[k].bEndpointAddress
+ );
+
+ memcpy(requestedEndpoints + endpoint_number, interface_driver->requestedEndpoints + k,
+ sizeof(struct usbd_endpoint_request));
+
+ requestedEndpoints[endpoint_number].interface_num += wIndex;
+ }
+
+ interface_number += interface_driver->interfaces;
+ }
+
+ if (composite_driver->class_name) {
+
+ struct usbd_class_instance *class_instance = (struct usbd_class_instance *)(interfaces_array + function_number);
+ struct usbd_class_driver *class_driver;
+
+ THROW_UNLESS(class_driver = usbp_find_class_driver(composite_driver->class_name), error);
+
+ class_instance->function.function_driver = (struct usbd_function_driver *)composite_driver;
+ class_instance->function.function_type = function_composite;
+ class_instance->function.name = composite_driver->driver.name;
+ class_instance->function.bus = bus;
+ alloc_usb_strings((struct usbd_function_instance *)class_instance, class_driver->driver.usb_string_indexes,
+ class_driver->driver.usb_strings);
+ }
+
+ return composite_instance;
+
+ CATCH(error) {
+ TRACE_MSG0(USBD, "FAILED");
+ //if (configuration_descriptor) LKFREE(configuration_descriptor);
+ if (composite_instance) {
+ usbd_strings_exit(bus);
+ for (i = 0; i < 2; i++) if (composite_instance->configuration_descriptor[i])
+ LKFREE(composite_instance->configuration_descriptor[i]);
+ }
+
+ if (requestedEndpoints) LKFREE(requestedEndpoints);
+ if (interfaces_array) LKFREE(interfaces_array);
+ if (interface_list) LKFREE(interface_list);
+ return NULL;
+ }
+}
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Function Registration:
+ * usbd_register_function_function()
+ * usbd_register_interface_function()
+ * usbd_register_composite_function()
+ * usbd_deregister_function()
+ * usbd_interface_deregister_function()
+ * usbd_class_deregister_function()
+ * usbd_composite_deregister_function()
+ * usbd_find_function()
+ * usbd_find_interface_function()
+ *
+ */
+
+/*!
+ * @brief usbd_find_function_internal() - search a usbd_function_driver instance by name
+ *
+ * USBD Function drivers call this to register with the USBD Core layer.
+ * Return NULL on failure.
+ *
+ * @param usbd_function_drivers - registerd usbd_function_driver table
+ * @param name - name to search
+ * @return function instance
+ *
+ */
+struct usbd_function_driver *
+usbd_find_function_internal(struct otg_list_node *usbd_function_drivers, const char *name)
+{
+ int len = name ? strlen(name) : 0;
+ struct otg_list_node *lhd = NULL;
+
+ #if defined(CONFIG_OTG_TRACE)
+ TRACE_STRING(USBD, "name:: %s", (char *)name);
+ #endif
+
+ LIST_FOR_EACH (lhd, usbd_function_drivers) {
+
+ struct usbd_function_driver *function_driver = LIST_ENTRY (lhd, struct usbd_function_driver, drivers);
+
+ CONTINUE_IF(len && strncmp(function_driver->name, name, len));
+
+ #if defined(CONFIG_OTG_TRACE)
+ TRACE_MSG1(USBD, "FOUND: %s", function_driver->name);
+ #endif
+
+ return function_driver;
+ }
+ return NULL;
+}
+
+
+/*!
+ * usbd_function_name() - return name of nth function driver
+ * @param n index to function name
+ * @return pointer to name
+ */
+char * usbd_function_name(int n)
+{
+ struct usbd_function_driver *function_driver = NULL;
+ struct otg_list_node *lhd = NULL;
+
+ TRACE_MSG1(USBD, "n: %d", n);
+ LIST_FOR_EACH (lhd, &usbd_simple_drivers) {
+ function_driver = LIST_ENTRY (lhd, struct usbd_function_driver, drivers);
+ TRACE_MSG2(USBD, "simple name: [%d] %s", n, function_driver->name);
+ CONTINUE_IF(n--);
+ TRACE_MSG1(USBD, "simple: %s", function_driver->name);
+ return (char *)function_driver->name;
+ };
+
+ LIST_FOR_EACH (lhd, &usbd_composite_drivers) {
+ function_driver = LIST_ENTRY (lhd, struct usbd_function_driver, drivers);
+ TRACE_MSG2(USBD, "composite name: [%d] %s", n, function_driver->name);
+ CONTINUE_IF(n--);
+ TRACE_MSG1(USBD, "composite: %s", function_driver->name);
+ return (char *)function_driver->name;
+ };
+
+ return NULL;
+}
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * String Descriptors:
+ * usbd_alloc_string_descriptor()
+ * usbd_free_string_descriptor()
+ * usbd_get_string_descriptor()
+ */
+
+#define LANGID_ENGLISH "\011"
+#define LANGID_US_ENGLISH "\004"
+#define LANGIDs LANGID_US_ENGLISH LANGID_ENGLISH
+
+/*!
+ * usbd_alloc_string_zero() - allocate a string descriptor and return index number
+ *
+ * Find an empty slot in index string array, create a corresponding descriptor
+ * and return the slot number.
+ *
+ * @param str string to allocate
+ * @param bus
+ * @return the slot number
+ */
+static u8 usbd_alloc_string_zero (struct usbd_bus_instance *bus, char *str)
+{
+ u8 bLength;
+ //u16 *wData;
+ struct usbd_langid_descriptor *langid = NULL;
+ unsigned char *cp;
+
+ RETURN_ZERO_IF(bus->usb_strings[0] != NULL);
+
+ TRACE_MSG2(USBD, "LANGID: %02x %02x", str[0], str[1]);
+
+ bLength = 4;
+
+ RETURN_ZERO_IF(!(langid = (struct usbd_langid_descriptor *) CKMALLOC (bLength)));
+
+
+ langid->bLength = bLength; // set descriptor length
+ langid->bDescriptorType = USB_DT_STRING; // set descriptor type
+ langid->bData[0] = str[1];
+ langid->bData[1] = str[0];
+
+ bus->usb_strings[0] = (struct usbd_string_descriptor *)langid; // save in string index array
+
+ TRACE_MSG4(USBD, "LANGID String: %02x %02x %02x %02x",
+ langid->bLength, langid->bDescriptorType, langid->bData[0], langid->bData[1]);
+ cp = (char *) langid;
+ TRACE_MSG4(USBD, "LANGID String: %02x %02x %02x %02x", cp[0], cp[1], cp[2], cp[3]);
+
+ return 0;
+}
+
+/*!
+ * usbd_strings_init() - initialize usb strings pool
+ */
+int usbd_strings_init(struct usbd_bus_instance *bus, struct usbd_function_instance *function_instance)
+{
+ bus->usbd_maxstrings = 50; // XXX Need configuration parameter...
+
+ RETURN_EINVAL_IF (!(bus->usb_strings = CKMALLOC (
+ sizeof (struct usbd_string_descriptor *) * bus->usbd_maxstrings)));
+
+ #if defined(CONFIG_OTG_LANGID)
+ {
+ char langid_str[4];
+ u16 langid;
+ TRACE_MSG1(USBD, "LANGID: %04x", CONFIG_OTG_LANGID);
+ langid = cpu_to_le16(CONFIG_OTG_LANGID);
+ memset(langid_str, 0, sizeof(langid_str));
+ langid_str[0] = langid & 0xff;
+ langid_str[1] = (langid >> 8) & 0xff;
+ if (usbd_alloc_string_zero(bus, langid_str)) {
+ LKFREE (function_instance->bus->usb_strings);
+ return -EINVAL;
+ }
+ }
+ #else /* defined(CONFIG_OTG_LANGID) */
+
+ if (usbd_alloc_string_zero(ubs, LANGIDs) != 0) {
+ LKFREE (function_instance->bus->usb_strings);
+ return -EINVAL;
+ }
+ #endif /* defined(CONFIG_OTG_LANGID) */
+ return 0;
+}
+
+/*!
+ * usbd_strings_exit() - de-initialize usb strings pool
+ */
+void usbd_strings_exit(struct usbd_bus_instance *bus)
+{
+ int i;
+ RETURN_IF (!bus->usb_strings);
+ for (i = 0; i < bus->usbd_maxstrings; i++)
+ usbd_free_string_descriptor(bus, i);
+ LKFREE (bus->usb_strings);
+ bus->usb_strings = NULL;
+}
+
+
+/*!
+ * usbd_get_string_descriptor() - find and return a string descriptor
+ *
+ * Find an indexed string and return a pointer to a it.
+ * @param function_instance
+ * @param index index of string
+ * @return pointer to string descriptor or NULL
+ */
+struct usbd_string_descriptor *usbd_get_string_descriptor (struct usbd_function_instance *function_instance, u8 index)
+{
+ RETURN_NULL_IF (index >= function_instance->bus->usbd_maxstrings);
+ return function_instance->bus->usb_strings[index];
+}
+
+/*!
+ * usbd_realloc_string() - allocate a string descriptor and return index number
+ *
+ * Find an empty slot in index string array, create a corresponding descriptor
+ * and return the slot number.
+ * @param function_instance
+ * @param index
+ * @param str
+ * @return new index
+ */
+u8 usbd_realloc_string (struct usbd_function_instance *function_instance, u8 index, const char *str)
+{
+ const char *save = str;
+ struct usbd_string_descriptor *string;
+ u8 bLength;
+ u16 *wData;
+
+ RETURN_EINVAL_IF(!str || !strlen (str));
+
+ // XXX should have semaphores...
+
+ if ((index < function_instance->bus->usbd_maxstrings) && (string = function_instance->bus->usb_strings[index])) {
+ function_instance->bus->usb_strings[index] = NULL;
+ LKFREE (string);
+ }
+
+ bLength = USBD_STRING_SIZEOF (struct usbd_string_descriptor) + 2 * strlen (str);
+
+ RETURN_ENOMEM_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength)));
+
+ string->bLength = bLength;
+ string->bDescriptorType = USB_DT_STRING;
+
+ for (wData = string->wData; *str;)
+ *wData++ = cpu_to_le16 ((u16) (*str++));
+
+ // store in string index array
+ function_instance->bus->usb_strings[index] = string;
+
+ TRACE_MSG2(USBD, "STRING[%d] %s", index, save);
+ return index;
+}
+
+/*!
+ * @brief strtest() - check if a string exists in string descriptor table
+ *
+ * @param string - string descriptor table
+ * @param str - string to test
+ * @return non-zero if match
+ */
+int strtest(struct usbd_string_descriptor *string, const char *str)
+{
+ int i;
+ for (i = 0; i < string->bLength; i++ ) {
+ CONTINUE_IF ( (*str == (string->wData[i] & 0xff)));
+ return 1;
+ }
+ return 0;
+}
+
+
+/*!
+ * usbd_alloc_string() - allocate a string descriptor and return index number
+ *
+ * Find an empty slot in index string array, create a corresponding descriptor
+ * and return the slot number.
+ * @param function_instance
+ * @param str
+ * @return index
+ *
+ * XXX rename to usbd_alloc_string_descriptor
+ */
+u8 usbd_alloc_string (struct usbd_function_instance *function_instance, const char *str)
+{
+ //const char *save = str;
+ //int i, j;
+ int i;
+ struct usbd_string_descriptor *string = NULL;
+ u8 bLength;
+ u16 *wData;
+
+ TRACE_MSG2(USBD, "function_instance: %x str: %x", function_instance, str);
+
+ RETURN_ZERO_IF(!str || !strlen (str));
+
+ /* find an empty string descriptor slot
+ * "0" is consumed by the language ID, "1" is the optional IP address
+ * and "2" is for the product string (as required by the PST group)
+ */
+ for (i = 3; i < function_instance->bus->usbd_maxstrings; i++) {
+
+ if (function_instance->bus->usb_strings[i]) {
+ UNLESS (strtest(function_instance->bus->usb_strings[i], str))
+ return i;
+ continue;
+ }
+
+ bLength = USBD_STRING_SIZEOF (struct usbd_string_descriptor) + 2 * strlen (str);
+
+ RETURN_ZERO_IF(!(string = (struct usbd_string_descriptor *)CKMALLOC (bLength)));
+
+ string->bLength = bLength;
+ string->bDescriptorType = USB_DT_STRING;
+
+ for (wData = string->wData; *str;)
+ *wData++ = cpu_to_le16((u16) (*str++));
+
+ // store in string index array
+ function_instance->bus->usb_strings[i] = string;
+ //TRACE_MSG2(USBD, "STRING[%d] %s", i, save);
+ return i;
+ }
+ return 0;
+}
+
+
+/*!
+ * usbd_free_string_descriptor() - deallocate a string descriptor
+ *
+ * Find and remove an allocated string.
+ * @param bus
+ * @param index
+ *
+ */
+void usbd_free_string_descriptor (struct usbd_bus_instance *bus, u8 index)
+{
+ struct usbd_string_descriptor *string;
+
+ if ((index < bus->usbd_maxstrings) && (string = bus->usb_strings[index])) {
+ bus->usb_strings[index] = NULL;
+ LKFREE (string);
+ }
+}
+
+OTG_EXPORT_SYMBOL(usbd_realloc_string);
+OTG_EXPORT_SYMBOL(usbd_alloc_string);
+OTG_EXPORT_SYMBOL(usbd_free_string_descriptor);
+OTG_EXPORT_SYMBOL(usbd_get_string_descriptor);
+//OTG_EXPORT_SYMBOL(usbd_maxstrings);
+
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Device Information:
+ * usbd_high_speed()
+ * usbd_get_bMaxPower()
+ * usbd_endpoint_wMaxPacketsize()
+ * usbd_endpoint_wMaxPacketsize_ep0()
+ * usbd_endpoint_bEndpointAddress()
+ *
+ */
+
+
+/*!
+ * usbd_high_speed() - return high speed status
+ * @return true if operating at high speed
+ */
+BOOL usbd_high_speed(struct usbd_function_instance *function)
+{
+ // XXX TODO - per function modifications for composite devices
+ return BOOLEAN(function->bus->high_speed);
+}
+
+/*!
+ * usbd_get_bMaxPower() - process a received urb
+ *
+ * Used by a USB Bus interface driver to pass received device request to
+ * the appropriate USB Function driver.
+ *
+ * @param function
+ * @return non-zero if errror
+ */
+int usbd_get_bMaxPower(struct usbd_function_instance *function)
+{
+ struct usbd_bus_instance *bus = function->bus;
+ return bus->driver->bMaxPower;
+}
+
+/*! usbd_endpoint_map()
+ *
+ * @param function
+ * @return pointer to endpoint map
+ */
+struct usbd_endpoint_map *usbd_endpoint_map(struct usbd_function_instance *function)
+{
+ struct usbd_endpoint_map *endpoint_map = NULL;
+ RETURN_NULL_UNLESS(function);
+ switch(function->function_type) {
+ case function_simple:
+ RETURN_NULL_UNLESS((endpoint_map = ((struct usbd_simple_instance *)function)->endpoint_map_array));
+ return endpoint_map;
+ case function_composite:
+ RETURN_NULL_UNLESS((endpoint_map = ((struct usbd_composite_instance *)function)->endpoint_map_array));
+ return endpoint_map;
+ case function_interface:
+ RETURN_NULL_UNLESS((endpoint_map = ((struct usbd_interface_instance *)function)->endpoint_map_array));
+ return endpoint_map;
+ case function_ep0:
+ RETURN_NULL_UNLESS((endpoint_map = ((struct usbd_interface_instance *)function)->endpoint_map_array));
+ return endpoint_map;
+ default:
+ return NULL;
+ }
+}
+
+/*!
+ * usbd_endpoint_wMaxPacketSize() - get maximum packet size for endpoint
+ * @param function
+ * @param endpoint_index
+ * @param hs highspeed flag
+ * @return endpoint size
+ */
+int usbd_endpoint_wMaxPacketSize(struct usbd_function_instance *function, int endpoint_index, int hs)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ //TRACE_MSG4(USBD, "function: %x index: %d endpoint_map: %x wMaxPacketSize: %02x",
+ // function, endpoint_index, endpoint_map, endpoint_map[endpoint_index].wMaxPacketSize[hs]);
+ //return le16_to_cpu(endpoint_map[endpoint_index].wMaxPacketSize[hs]);
+ return endpoint_map[endpoint_index].wMaxPacketSize[hs];
+}
+
+/*!
+ * usbd_endpoint_zero_wMaxPacketSize() - get maximum packet size for endpoint zero
+ * @param function
+ * @param hs highspeed flag
+ * @return endpoint size
+ */
+int usbd_endpoint_zero_wMaxPacketSize(struct usbd_function_instance *function, int hs)
+{
+ struct usbd_endpoint_map *endpoint_map;
+ RETURN_ZERO_IF(!(endpoint_map = function->bus->ep0->endpoint_map_array));
+ //return le16_to_cpu(endpoint_map[0].wMaxPacketSize[hs]);
+ return endpoint_map[0].wMaxPacketSize[hs];
+}
+
+/*!
+ * usbd_endpoint_bEndpointAddress() - get endpoint addrsess
+ * @param function
+ * @param endpoint_index
+ * @param hs high speed flag
+ * @return endpoint address
+ */
+int usbd_endpoint_bEndpointAddress(struct usbd_function_instance *function, int endpoint_index, int hs)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ //TRACE_MSG3(USBD, "function: %x index: %d endpoint_map: %x", function, endpoint_index, endpoint_map);
+ return endpoint_map[endpoint_index].bEndpointAddress[hs];
+}
+
+OTG_EXPORT_SYMBOL(usbd_endpoint_map);
+OTG_EXPORT_SYMBOL(usbd_endpoint_wMaxPacketSize);
+OTG_EXPORT_SYMBOL(usbd_endpoint_zero_wMaxPacketSize);
+OTG_EXPORT_SYMBOL(usbd_endpoint_bEndpointAddress);
+OTG_EXPORT_SYMBOL(usbd_high_speed);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+
+/*!
+ * @name STRUCT
+ * @brief
+ * Structure member address manipulation macros.
+ *
+ * These are used by client code (code using the urb_link routines), since
+ * the urb_link structure is embedded in the client data structures.
+ *
+ * Note: a macro offsetof equivalent to member_offset is defined in stddef.h
+ * but this is kept here for the sake of portability.
+ *
+ * p2surround returns a pointer to the surrounding structure given
+ * type of the surrounding structure, the name memb of the structure
+ * member pointed at by ptr. For example, if you have:
+ *
+ * struct foo {
+ * int x;
+ * float y;
+ * char z;
+ * } thingy;
+ *
+ * char *cp = &thingy.z;
+ *
+ * then
+ * &thingy == p2surround(struct foo, z, cp)
+ *
+ * @{
+ */
+
+#define _cv_(ptr) ((char*)(void*)(ptr))
+#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0)
+#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb)))
+
+/*!
+ * @name list
+ * @brief List support functions
+ *
+ * @{
+ */
+
+/*!
+ * @brief urb_link() - return first urb_link in list
+ *
+ * Return the first urb_link in a list with a distinguished
+ * head "hd", or NULL if the list is empty. This will also
+ * work as a predicate, returning NULL if empty, and non-NULL
+ * otherwise.
+ *
+ * Called from interrupt.
+ *
+ * @param hd
+ * @return link to urb
+ */
+static INLINE urb_link *first_urb_link (urb_link * hd)
+{
+ urb_link *nx;
+ return (!hd || !(nx = hd->next) || (nx == hd)) ? NULL : nx;
+}
+
+/*!
+ * @brief _usbd_first_urb() - return first urb in list
+ *
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ * Called from interrupt.
+ *
+ * @param hd linked list of urbs
+ * @return pointer to urb
+ */
+struct usbd_urb *_usbd_first_urb(urb_link * hd)
+{
+ urb_link *nx = first_urb_link (hd);
+ struct usbd_urb *urb = nx ? p2surround (struct usbd_urb, link, nx) : NULL;
+ return urb;
+}
+
+#if 0
+/*!
+ * @brief _usbd_unlink_urb() - return first urb in list
+ *
+ * Return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ *
+ * Called from interrupt.
+ *
+ * @param urb linked list of urbs
+ * @return pointer to urb
+ */
+void INLINE _usbd_unlink_urb(struct usbd_urb *urb)
+{
+ urb_link *ul = &urb->link;
+ ul->next->prev = ul->prev;
+ ul->prev->next = ul->next;
+ ul->prev = ul->next = ul;
+}
+#endif
+
+/*!
+ * @brief usbd_first_urb_detached() - detach an return first urb
+ *
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ * @param endpoint pointer to endpoint instance
+ * @param hd linked list of urbs
+ * @return pointer to urb or NULL
+ */
+struct usbd_urb *usbd_first_urb_detached (struct usbd_endpoint_instance *endpoint, urb_link * hd)
+{
+ struct usbd_urb *urb;
+ otg_pthread_mutex_lock(&endpoint->mutex); /* lock mutex */
+ if ((urb = _usbd_first_urb (hd))) {
+ usbd_unlink_urb(urb);
+ hd->total-=1;
+ }
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+ return urb;
+}
+
+/* @} */
+
+
+/*!
+ * @brief usbd_first_finished_urb_detached() - detach an return first urb
+ *
+ * Detach and return the first urb in a list with a distinguished
+ * head "hd", or NULL if the list is empty.
+ * @param endpoint pointer to endpoint instance
+ * @param hd linked list of urbs
+ * @return pointer to urb or NULL
+ */
+struct usbd_urb *usbd_first_finished_urb_detached (struct usbd_endpoint_instance *endpoint, urb_link * hd)
+{
+ struct usbd_urb *urb;
+
+ otg_pthread_mutex_lock(&endpoint->mutex); /* lock mutex */
+ if ((urb = _usbd_first_urb(hd))) {
+ //TRACE_MSG0(USBD,"enter1");
+ if (urb->irq_flags == USBD_URB_FINISHED) {
+ //TRACE_MSG0(USBD,"enter2");
+ usbd_unlink_urb(urb);
+ hd->total-=1;
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+ //TRACE_MSG1(USBD,"return urb:%x",urb);
+ return urb;
+ }
+ }
+ //TRACE_MSG0(USBD,"return NULL");
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+ return NULL;
+}
+
+/*!
+ * @brief usbd_first_ready_urb() - detach an return first urb
+ *
+ * Find the first ready urb, mark as active and return pointer.
+ *
+ * N.B. must be called with interrupts locked.
+ * @param endpoint pointer to endpoint instance
+ * @param hd linked list of urbs
+ * @return pointer to urb or NULL
+ */
+struct usbd_urb *usbd_first_ready_urb(struct usbd_endpoint_instance *endpoint, urb_link * hd)
+{
+ //struct usbd_urb *urb;
+ urb_link *ul;
+ otg_pthread_mutex_lock(&endpoint->mutex); /* lock mutex */
+ for (ul = hd->next; ul != hd; ) {
+ struct usbd_urb *urb = p2surround (struct usbd_urb, link, ul);
+ ul = urb->link.next;
+ CONTINUE_UNLESS(urb->irq_flags & USBD_URB_READY);
+ urb->irq_flags |= USBD_URB_ACTIVE;
+ //hd->total-=1;
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+
+ return urb;
+ }
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+ return NULL;
+}
+
+/* ************************************************************************** */
+/*!
+ * Device I/O:
+ * usbd_urb_finished()
+ * usbd_first_urb_detached()
+ * usbd_find_endpoint_address()
+ */
+
+/*!
+ * @brief urb_append() - append a linked list of urbs to another linked list
+ * @param hd link to urb list
+ * @param urb to append to list
+ */
+void urb_append (urb_link * hd, struct usbd_urb *urb)
+{
+ struct usbd_endpoint_instance *endpoint = urb->endpoint;
+ urb_link *new;
+ urb_link *pul;
+ RETURN_UNLESS (hd && urb);
+ otg_pthread_mutex_lock(&endpoint->mutex); /* lock mutex */
+ new = &urb->link;
+ pul = hd->prev;
+ new->prev->next = hd;
+ hd->prev = new->prev;
+ new->prev = pul;
+ pul->next = new;
+ hd->total+=1;
+ otg_pthread_mutex_unlock(&endpoint->mutex); /* unlock mutex */
+}
+
+
+/* ********************************************************************************************* */
+/*!
+ * Device Control:
+ *
+ * XXX usbd_request_endpoints()
+ * XXX usbd_configure_endpoints()
+ *
+ * usbd_get_device_state()
+ * usbd_get_device_status()
+ * usbd_framenum()
+ * usbd_ticks()
+ * usbd_elapsed()
+ */
+
+/*!
+ * usbd_flush_endpoint() - flush urbs from endpoint
+ *
+ * Iterate across the approrpiate tx or rcv list and cancel any outstanding urbs.
+ *
+ * @param endpoint flush urbs on this endpoint
+ */
+void usbd_flush_endpoint (struct usbd_endpoint_instance *endpoint)
+{
+ struct usbd_urb *urb;
+
+ if (TRACE_VERBOSE)
+ if (endpoint->bEndpointAddress)
+ TRACE_MSG1(USBD, "bEndpointAddress: %02x", endpoint->bEndpointAddress);
+
+ while ((urb = endpoint->active_urb))
+ usbd_cancel_urb(urb);
+
+ for (; (urb = usbd_first_urb_detached (endpoint, &endpoint->rdy)); usbd_cancel_urb(urb))
+ TRACE_MSG1(USBD, "cancelled urb:%x", urb);
+}
+
+
+/*!
+ * usbd_flush_endpoint_address() - flush endpoint
+ * @param function
+ * @param endpoint_index
+ */
+void usbd_flush_endpoint_index (struct usbd_function_instance *function, int endpoint_index)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ struct usbd_endpoint_instance *endpoint;
+
+ RETURN_UNLESS(function && endpoint_map);
+ RETURN_IF(!(endpoint = endpoint_map[endpoint_index].endpoint));
+ usbd_flush_endpoint (endpoint);
+}
+
+/*!
+ * usbd_flush_endpoint_address() - flush endpoint
+ * @param function
+ * @param endpoint_index
+ */
+int usbd_endpoint_urb_num(struct usbd_function_instance *function, int endpoint_index)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ struct usbd_endpoint_instance *endpoint;
+ if (!(function && endpoint_map)) return -1;
+ if (!(endpoint = endpoint_map[endpoint_index].endpoint)) return -1;
+ return ((endpoint->rdy).total);
+}
+
+
+
+/*!
+ * usbd_get_device_state() - return device state
+ * @param function
+ * @return current device state
+ */
+usbd_device_state_t usbd_get_device_state(struct usbd_function_instance *function)
+{
+ return (function && function->bus) ? function->bus->device_state : STATE_UNKNOWN;
+}
+
+/*!
+ * usbd_get_device_status() - return device status
+ * @param function
+ * @return current device status
+ */
+usbd_device_status_t usbd_get_device_status(struct usbd_function_instance *function)
+{
+ return (function && function->bus) ? function->bus->status : USBD_UNKNOWN;
+}
+
+OTG_EXPORT_SYMBOL(usbd_first_ready_urb);
+OTG_EXPORT_SYMBOL(usbd_first_finished_urb_detached);
+OTG_EXPORT_SYMBOL(usbd_get_device_state);
+OTG_EXPORT_SYMBOL(usbd_get_device_status);
+OTG_EXPORT_SYMBOL(usbd_flush_endpoint);
+OTG_EXPORT_SYMBOL(usbd_flush_endpoint_index);
+OTG_EXPORT_SYMBOL(usbd_endpoint_urb_num);
+
+/* ********************************************************************************************* */
+/* ********************************************************************************************* */
+/*!
+ * Endpoint I/O:
+ * usbd_alloc_urb()
+ * usbd_start_in_urb()
+ * usbd_start_out_urb()
+ * usbd_cancel_urb()
+ *
+ */
+
+/*!
+ * usbd_alloc_urb() - allocate an URB appropriate for specified endpoint
+ *
+ * Allocate an urb structure. The usb device urb structure is used to
+ * contain all data associated with a transfer, including a setup packet for
+ * control transfers.
+ *
+ * @param function
+ * @param endpoint_index
+ * @param length
+ * @param notify
+ * @return urb
+ */
+struct usbd_urb *usbd_alloc_urb (struct usbd_function_instance *function, int endpoint_index,
+ int length, usbd_urb_notification *notify)
+{
+ struct usbd_urb *urb = NULL;
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ struct usbd_endpoint_instance *endpoint;
+ int hs = function->bus->high_speed;
+
+
+ RETURN_NULL_IF(!(endpoint = endpoint_map[endpoint_index].endpoint));
+
+ THROW_IF (!(urb = (struct usbd_urb *)CKMALLOC (sizeof (struct usbd_urb))), error);
+ urb->endpoint = endpoint;
+ urb->endpoint_index = endpoint_index;
+ urb->wMaxPacketSize = endpoint->wMaxPacketSize[hs];
+ urb->bus = function->bus;
+ urb->function_instance = function;
+ urb->link.prev = urb->link.next = &urb->link;
+ urb->notify = notify;
+ urb->dma_addr = (dma_addr_t)NULL;
+ urb->alloc_length = urb->actual_length = urb->buffer_length = 0;
+
+ if (length) {
+
+ urb->buffer_length = length;
+
+ /* For receive we always overallocate to ensure that receiving another
+ * full sized packet when we are only expecting a short packet will
+ * not overflow the buffer. Endpoint zero alloc's don't specify direction
+ * so always overallocate.
+ */
+ UNLESS (endpoint->bEndpointAddress[hs] && (endpoint->bEndpointAddress[hs] & USB_ENDPOINT_DIR_MASK))
+ length = ((length / urb->wMaxPacketSize) + 3) * urb->wMaxPacketSize;
+
+ THROW_IF(!(urb->buffer = (u8 *)KMALLOC (length)), error);
+ urb->alloc_length = length;
+ }
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG7(USBD, "[%2x] urb:%x endpoint_index: %d "
+ "length: %d wMaxPacketSize: %d buffer_length: %d alloc_length: %d",
+ endpoint->bEndpointAddress[hs],
+ urb, endpoint_index, length, urb->wMaxPacketSize, urb->buffer_length, urb->alloc_length);
+
+ CATCH(error) {
+
+ #if defined(OTG_LINUX)
+ #endif /* defined(OTG_LINUX) */
+ #if defined(OTG_WINCE)
+ DEBUGMSG(ZONE_INIT, (_T("usbd_alloc_urb: FAILED\n")));
+ #endif /* defined(OTG_LINUX) */
+ usbd_free_urb(urb);
+ urb = NULL;
+ TRACE_MSG0(USBD, "CATCH a error\n");
+ }
+ return urb;
+}
+
+/*!
+ * usbd_halt_endpoint() -
+ *
+ * @param function function that owns endpoint
+ * @param endpoint_index endpoint number
+ * @return non-zero if endpoint is halted.
+ */
+int usbd_halt_endpoint (struct usbd_function_instance *function, int endpoint_index)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ struct usbd_endpoint_instance *endpoint;
+ int hs = function->bus->high_speed;
+
+ RETURN_ZERO_UNLESS(function && endpoint_map);
+ RETURN_ZERO_IF(!(endpoint = endpoint_map[endpoint_index].endpoint));
+
+ return function->bus->driver->bops->halt_endpoint (function->bus, endpoint->bEndpointAddress[hs], 1);
+}
+
+
+/*!
+ * usbd_alloc_urb_ep0() - allocate an urb for endpoint zero
+ * @param function
+ * @param length
+ * @param callback
+ * @return urb
+ */
+struct usbd_urb *usbd_alloc_urb_ep0 (struct usbd_function_instance *function, int length,
+ int (*callback) (struct usbd_urb *, int))
+{
+ return usbd_alloc_urb((struct usbd_function_instance *)function->bus->ep0, 0, length, callback);
+}
+
+/*!
+ * usbd_free_urb() - deallocate an URB and associated buffer
+ *
+ * Deallocate an urb structure and associated data.
+ * @param urb
+ */
+void usbd_free_urb (struct usbd_urb *urb)
+{
+#if 0
+ RETURN_IF (!urb);
+ if (urb->buffer) LKFREE ((void *) urb->buffer);
+ LKFREE (urb);
+#else
+ RETURN_UNLESS(urb);
+ RETURN_IF(urb == urb->bus->ep0_urb);
+ if (urb->buffer) LKFREE(urb->buffer);
+ LKFREE (urb);
+#endif
+
+}
+/*! usbd_start_in_urb - called to send urb
+ * @param urb -pointer of usbd_urb to send
+ * @return send result or -EAGAIN on error
+ */
+int usbd_start_in_urb (struct usbd_urb *urb)
+{
+ int hs = urb->bus->high_speed;
+ struct usbd_bus_instance *bus = urb->bus;
+ struct usbd_endpoint_instance *endpoint= urb->endpoint;
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG6(USBD, "[%2x] urb: %x index: %d bus: %x status: %d length: %d",
+ urb->endpoint->bEndpointAddress[hs], urb, urb->endpoint_index, urb->bus, urb->bus->status,
+ urb->actual_length);
+
+ if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) {
+ urb->status = USBD_URB_STALLED;
+ TRACE_MSG1(USBD, "[%2x] USBD_URB_STALLED", urb->endpoint->bEndpointAddress[hs]);
+ return -EAGAIN;
+ }
+ if (urb->endpoint->bEndpointAddress[hs] && (USBD_OK != urb->bus->status)) {
+ urb->status = USBD_URB_NOT_READY;
+ TRACE_MSG1(USBD, "[%2x] USBD_URB_NOT_READY", urb->endpoint->bEndpointAddress[hs]);
+ return -EAGAIN;
+ }
+ //TRACE_MSG2(USBD, "endpoint: %02x length: %d", urb->endpoint->bEndpointAddress[hs], urb->actual_length);
+ urb->status = USBD_URB_QUEUED;
+ urb->flags |= USBD_URB_IN;
+ urb->irq_flags |= USBD_URB_READY;
+
+ /* N.B. Race condition possible after this, once appended to the ready queue it
+ * is possible for an interrupt handler to start using *and* complete this urb
+ * so it cannot be referenced after this point.
+ */
+ urb_append (&(urb->endpoint->rdy), urb);
+
+ /* Use saved values for bus and endpoint to call appropriate start out function
+ */
+ return bus->driver->bops->start_endpoint_in(bus, endpoint);
+}
+
+/*!
+ * usbd_start_out_urb() - recycle a received urb
+ *
+ * Used by a USB Function interface driver to recycle an urb.
+ *
+ * @param urb to process
+ * @return non-zero if error
+ */
+int usbd_start_out_urb (struct usbd_urb *urb)
+{
+ int hs = urb->bus->high_speed;
+ struct usbd_bus_instance *bus = urb->bus;
+ struct usbd_endpoint_instance *endpoint= urb->endpoint;
+
+ if (TRACE_VERBOSE)
+ TRACE_MSG6(USBD, "[%2x] urb: %x index: %d bus: %x status: %d length: %d",
+ urb->endpoint->bEndpointAddress[hs],
+ urb, urb->endpoint_index, urb->bus, urb->bus->status,
+ urb->buffer_length);
+ if (urb->endpoint->feature_setting & FEATURE(USB_ENDPOINT_HALT)) {
+ urb->status = USBD_URB_STALLED;
+ TRACE_MSG1(USBD, "[%2x] USBD_URB_STALLED", urb->endpoint->bEndpointAddress[hs]);
+ return -EAGAIN;
+ }
+ if (urb->endpoint->bEndpointAddress[hs] && (USBD_OK != urb->bus->status)) {
+ urb->status = USBD_URB_NOT_READY;
+ TRACE_MSG1(USBD, "[%2x] USBD_URB_NOT_READY", urb->endpoint->bEndpointAddress[hs]);
+ return -EAGAIN;
+ }
+
+ urb->actual_length = 0;
+ urb->status = USBD_URB_QUEUED;
+ urb->flags |= USBD_URB_OUT;
+ urb->irq_flags = USBD_URB_READY;
+
+ /* N.B. Race condition possible after this, once appended to the ready queue it
+ * is possible for an interrupt handler to start using *and* complete this urb
+ * so it cannot be referenced after this point.
+ */
+ urb_append (&(urb->endpoint->rdy), urb);
+
+ /* Use saved values for bus and endpoint to call appropriate start out function
+ */
+ return bus->driver->bops->start_endpoint_out(bus, endpoint);
+}
+
+/*!
+ * usbd_cancel_urb() - cancel an urb being sent
+ *
+ * @param urb to process
+ * @return non-zero if error
+ */
+int usbd_cancel_urb (struct usbd_urb *urb)
+{
+ RETURN_ZERO_UNLESS (urb && urb->bus && urb->bus->driver && urb->bus->driver->bops);
+ return urb->bus->driver->bops->cancel_urb_irq (urb);
+}
+
+
+/*!
+ * usbd_endpoint_halted() - return halt status
+ *
+ * @param function function that owns endpoint
+ * @param endpoint_index endpoint number
+ * @return non-zero if endpoint is halted.
+ */
+int usbd_endpoint_halted (struct usbd_function_instance *function, int endpoint_index)
+{
+ struct usbd_endpoint_instance *endpoint;
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ RETURN_ZERO_UNLESS(function && endpoint_map);
+ RETURN_ZERO_UNLESS((endpoint = endpoint_map[endpoint_index].endpoint));
+ return function->bus->driver->bops->endpoint_halted (function->bus,
+ endpoint->bEndpointAddress[function->bus->high_speed]);
+}
+
+
+OTG_EXPORT_SYMBOL(usbd_alloc_urb);
+OTG_EXPORT_SYMBOL(usbd_alloc_urb_ep0);
+OTG_EXPORT_SYMBOL(usbd_free_urb);
+
+OTG_EXPORT_SYMBOL(usbd_start_in_urb);
+OTG_EXPORT_SYMBOL(usbd_start_out_urb);
+OTG_EXPORT_SYMBOL(usbd_cancel_urb);
+OTG_EXPORT_SYMBOL(usbd_halt_endpoint);
+
+/* ********************************************************************************************* */
+
+/*!
+ * usbd_function_get_privdata() - get private data pointer
+ * @param function
+ * @return void * pointer to private data
+ */
+void *usbd_function_get_privdata(struct usbd_function_instance *function)
+{
+ return(function->privdata);
+}
+
+/*!
+ * usbd_function_set_privdata() - set private data structure in function
+ * @param function
+ * @param privdata
+ */
+void usbd_function_set_privdata(struct usbd_function_instance *function, void *privdata)
+{
+ function->privdata = privdata;
+}
+
+/*!
+ * usbd_endpoint_transferSize() - get transferSize for endpoint
+ * @param function
+ * @param endpoint_index
+ * @param hs highspeed flag
+ * @return transfer size
+ */
+int usbd_endpoint_transferSize(struct usbd_function_instance *function, int endpoint_index, int hs)
+{
+ struct usbd_endpoint_map *endpoint_map = usbd_endpoint_map(function);
+ RETURN_ZERO_UNLESS(function && endpoint_map);
+ return endpoint_map[endpoint_index].transferSize[hs];
+}
+
+/*!
+ * usbd_endpoint_update() - update endpoint address and size
+ * @param function
+ * @param endpoint_index
+ * @param endpoint descriptor
+ * @param hs high speed flag
+ */
+void usbd_endpoint_update(struct usbd_function_instance *function, int endpoint_index,
+ struct usbd_endpoint_descriptor *endpoint, int hs)
+{
+ endpoint->bEndpointAddress = usbd_endpoint_bEndpointAddress(function, endpoint_index, hs);
+ endpoint->wMaxPacketSize = usbd_endpoint_wMaxPacketSize(function, endpoint_index, hs);
+}
+
+/*!
+ * usbd_otg_bmattributes() - return attributes
+ * @param function
+ * @return endpoint attributes
+ */
+int usbd_otg_bmattributes(struct usbd_function_instance *function)
+{
+ // XXX TODO - per function modifications for composite devices
+ return function->bus->bmAttributes;
+}
+
+
+
+/*!
+ * usbd_write_info_message() -
+ *
+ * Send a message to the otg management application.
+ * @param function - function instance pointer
+ * @param msg
+ */
+void usbd_write_info_message(struct usbd_function_instance *function, char *msg)
+{
+ struct usbd_bus_instance *bus = function->bus;
+ RETURN_UNLESS(bus);
+ otg_write_info_message(bus->privdata, msg);
+}
+
+OTG_EXPORT_SYMBOL(usbd_function_get_privdata);
+OTG_EXPORT_SYMBOL(usbd_function_set_privdata);
+OTG_EXPORT_SYMBOL(usbd_endpoint_transferSize);
+OTG_EXPORT_SYMBOL(usbd_otg_bmattributes);
+OTG_EXPORT_SYMBOL(usbd_endpoint_update);
+OTG_EXPORT_SYMBOL(usbd_write_info_message);
diff --git a/drivers/otg/otgcore/usbp-procfs.c b/drivers/otg/otgcore/usbp-procfs.c
new file mode 100644
index 000000000000..b2779f920a2b
--- /dev/null
+++ b/drivers/otg/otgcore/usbp-procfs.c
@@ -0,0 +1,608 @@
+/*
+ * 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
+ */
+/*
+ * otg/otgcore/usbp-procfs.c - USB Device Core Layer
+ * @(#) balden@belcarra.com|otg/otgcore/usbp-procfs.c|20070327230504|28330
+ *
+ * Copyright (c) 2004-2005 Belcarra Technologies Corp
+ * Copyright (c) 2005-2006 Belcarra Technologies 2005 Corp
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>,
+ * Bruce Balden <balden@belcarra.com>
+ *
+ */
+/*!
+ * @file otg/otgcore/usbp-procfs.c
+ * @brief Implements /proc/usbd-functions, which displays descriptors for the current selected function.
+ *
+ *
+ * @ingroup USBDCORE
+ */
+
+#include <otg/otg-compat.h>
+
+//#ifdef LINUX24
+//EXPORT_NO_SYMBOLS;
+//#endif
+
+#include <otg/usbp-chap9.h>
+#include <otg/otg-trace.h>
+#include <otg/otg-api.h>
+#include <otg/otg-trace.h>
+#include <otg/usbp-func.h>
+#include <otg/usbp-bus.h>
+#include <otg/otg-pcd.h>
+
+
+#define MAX_INTERFACES 2
+
+static struct usbd_bus_instance *procfs_usbd_bus_instance;
+extern struct otg_list_node usbd_simple_drivers; // list of all registered configuration function modules
+extern struct otg_list_node usbd_interface_drivers; // list of all registered interface function modules
+extern struct otg_list_node usbd_class_drivers; // list of all registered composite function modules
+extern struct otg_list_node usbd_composite_drivers; // list of all registered composite function modules
+
+
+/*!
+ * list_function() - list registered usbd_function_driver from start information
+ *
+ * @param usbd_function_drivers - usbd function drivers table
+ * @param cp - buffer to store driver information
+ * @param start - start position of list
+ * @return information length
+ */
+int list_function_composite(struct otg_list_node *usbd_function_drivers, char *cp, int start)
+{
+ int total = 0;
+ int count = 0;
+
+ struct otg_list_node *lhd = NULL;
+
+
+ LIST_FOR_EACH (lhd, usbd_function_drivers) {
+ struct usbd_composite_driver *composite_driver =
+ (struct usbd_composite_driver *) LIST_ENTRY (lhd, struct usbd_function_driver, drivers);
+
+ const char **names = composite_driver->interface_function_names;
+
+ CONTINUE_IF(count++ < start);
+
+ total += sprintf(cp + total, "\t%20s", composite_driver->driver.name);
+ total += sprintf(cp + total, " %04x/%04x %02x/%02x/%02x ",
+ composite_driver->device_description->idVendor,
+ composite_driver->device_description->idProduct,
+ composite_driver->device_description->bDeviceClass,
+ composite_driver->device_description->bDeviceSubClass,
+ composite_driver->device_description->bDeviceProtocol
+ );
+ while ( *names ) {
+ total += sprintf(cp + total, "%s", *names++);
+ if (*names)
+ total += sprintf(cp + total, ":");
+ }
+
+
+ if (composite_driver ->class_name)
+ total += sprintf(cp + total, "; %s", composite_driver->class_name);
+
+ total += sprintf(cp + total, "\n");
+ break;
+ }
+ return total;
+}
+
+/*!
+ * list_function() - list driver's name from usbd_function_drivers table
+ */
+int list_function(struct otg_list_node *usbd_function_drivers, char *cp, char *msg)
+{
+ int total = sprintf(cp, "\n%s\n", msg);
+
+ struct otg_list_node *lhd = NULL;
+ LIST_FOR_EACH (lhd, usbd_function_drivers) {
+ struct usbd_function_driver *function_driver = LIST_ENTRY (lhd, struct usbd_function_driver, drivers);
+ total += sprintf(cp + total, "\t%s\n", function_driver->name);
+ }
+ return total;
+}
+
+
+/* Proc Filesystem *************************************************************************** */
+/*!
+ * dohexdigit - change a value to hexdecimal char
+ * @param cp - buffer to store transformed char
+ * @param val - value to process
+ *
+ */
+static void dohexdigit (char *cp, unsigned char val)
+{
+ if (val < 0xa) {
+ *cp = val + '0';
+ } else if ((val >= 0x0a) && (val <= 0x0f)) {
+ *cp = val - 0x0a + 'a';
+ }
+}
+
+/*!
+ * dohex - using translate a unsigned char value to hex notation
+ * @param cp - buffer to save hex notation
+ * @param val - value to translate
+ *
+ */
+static void dohexval (char *cp, unsigned char val)
+{
+ dohexdigit (cp++, val >> 4);
+ dohexdigit (cp++, val & 0xf);
+}
+
+/*!
+ * dump_descriptor - create a descriptor for string sp
+ * @param buf - buffer to store descriptor
+ * @param sp -
+ * @return created descriptor length
+ */
+static int dump_descriptor (char *buf, char *sp)
+{
+ int num;
+ int len = 0;
+
+ RETURN_ZERO_UNLESS(sp);
+
+ num = *sp;
+
+ while (sp && num--) {
+ dohexval (buf, *sp++);
+ buf += 2;
+ *buf++ = ' ';
+ len += 3;
+ }
+ len++;
+ *buf = '\n';
+ return len;
+}
+
+/*!
+ * dump_string_descriptor - dispaly string descriptor information
+ * @param function_instance - function instance pointer
+ * @param buf - buffer to save information
+ * @param i -
+ * @param name -
+ * @return information buffer length
+ */
+static int dump_string_descriptor (struct usbd_function_instance *function_instance, char *buf, int i, char *name)
+{
+ int k;
+ int len = 0;
+
+ struct usbd_string_descriptor *string_descriptor;
+
+
+ RETURN_ZERO_UNLESS(i);
+
+ string_descriptor = usbd_get_string_descriptor (function_instance, i);
+
+ RETURN_ZERO_UNLESS (string_descriptor);
+
+ len += sprintf((char *)buf+len, " %-24s [%2d:%2d ] ", name, i, string_descriptor->bLength);
+
+ for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) {
+ *(char *) (buf + len) = (char) string_descriptor->wData[k];
+ len++;
+ }
+ len += sprintf ((char *) buf + len, "\n");
+
+ return len;
+}
+
+
+
+/*! dump_device_descriptors - dump device descriptor
+ * @param function_instance - usbd_function_instance pointer
+ * @param buf - buffer for saving descriptor information
+ * @param sp - device descriptor pointer
+ * @return buffer length
+ */
+static int dump_device_descriptor(struct usbd_function_instance *function_instance, char *buf, char *sp)
+{
+ int total = 0;
+ struct usbd_device_descriptor *device_descriptor = (struct usbd_device_descriptor *) sp;
+
+ TRACE_MSG2(USBD, "buf: %x sp: %x", buf, sp);
+
+ total = sprintf(buf + total, "Device descriptor [ ] ");
+ total += dump_descriptor(buf + total, sp);
+
+ total += sprintf(buf + total, " bcdUSB [ 2] %04x\n", device_descriptor->bcdUSB);
+ total += sprintf(buf + total, " bDevice[Class,Sub,Pro] [ 4] %02x %02x %02x\n",
+ device_descriptor->bDeviceClass, device_descriptor->bDeviceSubClass, device_descriptor->bDeviceProtocol);
+
+ total += sprintf(buf + total, " bMaxPacketSize0 [ 7] %02x\n", device_descriptor->bMaxPacketSize0);
+
+ total += sprintf(buf + total, " idVendor [ 8] %04x\n", device_descriptor->idVendor);
+ total += sprintf(buf + total, " idProduct [ 10] %04x\n", device_descriptor->idProduct);
+ total += sprintf(buf + total, " bcdDevice [ 12] %04x\n", device_descriptor->bcdDevice);
+
+ total += sprintf(buf + total, " bNumConfigurations [ 17] %02x\n", device_descriptor->bNumConfigurations);
+
+ total += dump_string_descriptor(function_instance, buf + total, device_descriptor->iManufacturer, "iManufacturer");
+ total += dump_string_descriptor(function_instance, buf + total, device_descriptor->iProduct, "iProduct");
+ total += dump_string_descriptor(function_instance, buf + total, device_descriptor->iSerialNumber, "iSerialNumber");
+
+ total += sprintf(buf + total, "\n");
+ return total;
+}
+
+/*! dump_device_qualifier_descriptors - dump device descriptor
+ * @param function_instance - usbd_function_instance pointer
+ * @param buf - buffer for saving descriptor information
+ * @param sp - device descriptor pointer
+ * @return buffer length
+ */
+static int dump_device_qualifier_descriptor(struct usbd_function_instance *function_instance, char *buf, char *sp)
+{
+ int total = 0;
+ struct usbd_device_qualifier_descriptor *device_qualifier_descriptor = (struct usbd_device_qualifier_descriptor *) sp;
+
+ TRACE_MSG2(USBD, "buf: %x sp: %x", buf, sp);
+
+ total = sprintf(buf + total, "Device qualifier [ ] ");
+ total += dump_descriptor(buf + total, sp);
+
+ total += sprintf(buf + total, " bcdUSB [ 2] %04x\n", device_qualifier_descriptor->bcdUSB);
+
+ total += sprintf(buf + total, " bDevice[Class,Sub,Pro] [ 4] %02x %02x %02x\n",
+ device_qualifier_descriptor->bDeviceClass,
+ device_qualifier_descriptor->bDeviceSubClass, device_qualifier_descriptor->bDeviceProtocol);
+
+ total += sprintf(buf + total, " bMaxPacketSize0 [ 7] %02x\n",
+ device_qualifier_descriptor->bMaxPacketSize0);
+
+ total += sprintf(buf + total, " bNumConfigurations [ 17] %02x\n",
+ device_qualifier_descriptor->bNumConfigurations);
+
+ total += sprintf(buf + total, "\n");
+ return total;
+}
+
+/*! dump_config_descriptors - dump config descriptor information
+ * @param function_instance -
+ * @param buf
+ * @param sp - configuartion descriptor pointer
+ * @return information length
+ */
+static int dump_config_descriptor(struct usbd_function_instance *function_instance, char *buf, char *sp)
+{
+ struct usbd_configuration_descriptor *config = (struct usbd_configuration_descriptor *) sp;
+ struct usbd_endpoint_descriptor *endpoint;
+ struct usbd_interface_descriptor *interface;
+ struct usbd_interface_association_descriptor *iad;
+
+ int wTotalLength = le16_to_cpu(config->wTotalLength);
+ int bConfigurationValue = config->bConfigurationValue;
+ int interface_num;
+ int class_num;
+ int endpoint_num;
+ int total;
+
+ TRACE_MSG3(USBD, "buf: %x sp: %x length: %d", buf, sp, wTotalLength);
+
+ interface_num = class_num = endpoint_num = 0;
+
+ for (total = 0; wTotalLength > 0; ) {
+ BREAK_UNLESS(sp[0]);
+ switch (sp[1]) {
+ case USB_DT_CONFIGURATION:
+ case USB_DT_OTHER_SPEED_CONFIGURATION:
+ interface_num = class_num = endpoint_num = 0;
+ total += sprintf(buf + total, "Configuration descriptor [%d ] ", bConfigurationValue);
+ break;
+ case USB_DT_INTERFACE:
+ class_num = 0;
+ total += sprintf(buf + total, "\nInterface descriptor [%d:%d:%d ] ",
+ bConfigurationValue, interface_num++, class_num);
+ break;
+ case USB_DT_ENDPOINT:
+ class_num = endpoint_num = 0;
+ total += sprintf(buf + total, "Endpoint descriptor [%d:%d:%d:%d] ",
+ bConfigurationValue, interface_num - 1, class_num, ++endpoint_num);
+ break;
+ case USB_DT_OTG:
+ class_num = endpoint_num = 0;
+ total += sprintf(buf + total, "OTG descriptor [%d ] ", bConfigurationValue);
+ break;
+ case USB_DT_INTERFACE_ASSOCIATION:
+ class_num = endpoint_num = 0;
+ total += sprintf(buf + total, "\nIAD descriptor [%d:%d ] ",
+ bConfigurationValue, interface_num);
+ break;
+ default:
+ endpoint_num = 0;
+ total += sprintf(buf + total, " Class descriptor [%d:%d:%d ] ",
+ bConfigurationValue, interface_num-1 , ++class_num);
+ break;
+ }
+ total += dump_descriptor(buf + total, sp);
+ switch (sp[1]) {
+ case USB_DT_CONFIGURATION:
+ case USB_DT_OTHER_SPEED_CONFIGURATION:
+ config = (struct usbd_configuration_descriptor *)sp;
+ total += sprintf(buf + total, " wTotalLength [ 4] %02x\n", config->wTotalLength);
+ total += sprintf(buf + total, " bNumInterfaces [ 4] %02x\n", config->bNumInterfaces);
+ total += sprintf(buf + total, " bConfigurationValue [ 5] %02x\n", config->bConfigurationValue);
+ total += dump_string_descriptor(function_instance, buf + total, config->iConfiguration, "iConfiguration");
+ total += sprintf(buf + total, " bmAttributes [ 7] %02x%s%s\n",
+ config->bmAttributes,
+ config->bmAttributes & USB_BMATTRIBUTE_SELF_POWERED ? " Self-Powered" : "",
+ config->bmAttributes & USB_BMATTRIBUTE_REMOTE_WAKEUP ? " Remote-Wakeup" : ""
+ );
+ total += sprintf(buf + total, " bMaxPower [ 8] %02x\n", config->bMaxPower);
+ break;
+ case USB_DT_INTERFACE:
+ interface = (struct usbd_interface_descriptor *)sp;
+ total += sprintf(buf + total, " bInterfaceNumber [ 2] %02x\n", interface->bInterfaceNumber);
+ total += sprintf(buf + total, " bAlternateSetting [ 5] %02x\n",
+ interface->bAlternateSetting);
+ total += sprintf(buf + total, " bNumEndpoints [ 5] %02x\n", interface->bNumEndpoints);
+ total += sprintf(buf + total, " bInterface[Class,Sub,Pro][ 5] %02x %02x %02x\n",
+ interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol);
+ total += dump_string_descriptor(function_instance, buf + total, interface->iInterface, "iInterface");
+ break;
+ case USB_DT_ENDPOINT:
+ endpoint = (struct usbd_endpoint_descriptor *)sp;
+ break;
+ case USB_DT_INTERFACE_ASSOCIATION:
+ iad = (struct usbd_interface_association_descriptor *)sp;
+ total += sprintf(buf + total, " bFirstInterface [ 2] %02x\n", iad->bFirstInterface);
+ total += sprintf(buf + total, " bInterfaceCount [ 3] %02x\n", iad->bInterfaceCount);
+ total += sprintf(buf + total, " bFunction[Class,Sub,Pro] [ 4] %02x %02x %02x\n",
+ iad->bFunctionClass, iad->bFunctionSubClass, iad->bFunctionProtocol);
+ total += dump_string_descriptor(function_instance, buf + total, iad->iFunction, "iFunction");
+ break;
+ default:
+ break;
+ }
+ wTotalLength -= sp[0];
+ sp += sp[0];
+ }
+ total += sprintf(buf + total, "\n");
+ return total;
+}
+
+/*!
+ * usbd_device_proc_read - implement proc file system read.
+ * @param page
+ * @param count
+ * @param pos
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return
+ * statistics for.
+ */
+int usbd_device_proc_read (char *page, size_t count, int * pos)
+{
+ int len = 0;
+ int index;
+
+ u8 config_descriptor[512];
+ int config_size;
+
+ struct usbd_function_instance *function_instance =
+ procfs_usbd_bus_instance && procfs_usbd_bus_instance->function_instance ?
+ procfs_usbd_bus_instance->function_instance : NULL;
+
+ struct pcd_instance *pcd_instance = (struct pcd_instance *)
+ procfs_usbd_bus_instance && procfs_usbd_bus_instance->privdata ?
+ procfs_usbd_bus_instance->privdata : NULL;
+
+ struct otg_instance *otg_instance = pcd_instance ? pcd_instance->otg : NULL;
+
+ //struct list_head *lhd;
+
+ len = 0;
+ index = (*pos)++;
+
+ switch(index) {
+
+ case 0:
+ #if defined(CONFIG_USB_PERIPHERAL)
+ len += sprintf ((char *) page + len, "USB Peripheral\n");
+ #elif defined(CONFIG_USB_PERIPHERAL_OR_HOST)
+ len += sprintf ((char *) page + len, "USB Peripheral or Host\n");
+ #elif defined(CONFIG_USB_WIRED_DEVICE)
+ len += sprintf ((char *) page + len, "USB Wired Device\n");
+ #elif defined(CONFIG_USB_WIRED_DEVICE_OR_HOST)
+ len += sprintf ((char *) page + len, "USB Wired Device or Host\n");
+ #elif defined(CONFIG_OTG_BDEVICE_WITH_SRP)
+ len += sprintf ((char *) page + len, "SRP Capable B-Device (only)\n");
+ #elif defined(CONFIG_OTG_DEVICE)
+ len += sprintf ((char *) page + len, "OTG Device\n");
+ #endif
+ len += sprintf ((char *) page + len, "usb-device list\n");
+ break;
+
+ case 1:
+ if ( function_instance) {
+ //int configuration = index;
+ //struct usbd_function_driver *function_driver;
+ int i;
+
+
+ if ((config_size = usbd_get_descriptor(function_instance, config_descriptor,
+ sizeof(config_descriptor),
+ USB_DT_DEVICE, 0)) > index) {
+ len += dump_device_descriptor(function_instance, (char *)page + len, config_descriptor );
+ }
+
+ if ((config_size = usbd_get_descriptor(function_instance, config_descriptor,
+ sizeof(config_descriptor),
+ USB_DT_CONFIGURATION, 0)) > index) {
+ len += dump_config_descriptor(function_instance, (char *)page + len, config_descriptor );
+ }
+
+ #ifdef CONFIG_OTG_HIGH_SPEED
+ len += sprintf ((char *) page + len, "High Speed\n");
+ if ((config_size = usbd_get_descriptor(function_instance, config_descriptor,
+ sizeof(config_descriptor),
+ USB_DT_DEVICE_QUALIFIER, 0)) > index) {
+ len += dump_device_qualifier_descriptor(function_instance,
+ (char *)page + len, config_descriptor );
+ }
+ len += sprintf ((char *) page + len, "Other Speed Descriptor\n");
+ if ((config_size = usbd_get_descriptor(function_instance, config_descriptor,
+ sizeof(config_descriptor),
+ USB_DT_OTHER_SPEED_CONFIGURATION, 0)) > index) {
+ len += dump_config_descriptor(function_instance, (char *)page + len, config_descriptor );
+ }
+ #endif /* CONFIG_OTG_HIGH_SPEED */
+
+ for (i = 0; i < procfs_usbd_bus_instance->usbd_maxstrings; i++) {
+
+ struct usbd_string_descriptor *string_descriptor
+ = usbd_get_string_descriptor (function_instance, i);
+ struct usbd_langid_descriptor *langid_descriptor =
+ (struct usbd_langid_descriptor *) string_descriptor;
+ int k;
+
+ CONTINUE_UNLESS (string_descriptor);
+
+ switch (i) {
+ case 0:
+ len += sprintf((char *)page+len,
+ "LangID [ 0 ] %2x:%2x %02x%02x\n",
+ langid_descriptor->bLength, langid_descriptor->bDescriptorType,
+ langid_descriptor->bData[0], langid_descriptor->bData[1]
+ );
+ break;
+ default:
+ len += sprintf((char *)page+len, "String [%2d:%2d ] ",
+ i, string_descriptor->bLength);
+
+ // bLength = sizeof(struct usbd_string_descriptor) + 2*strlen(str)-2;
+
+ for (k = 0; k < (string_descriptor->bLength / 2) - 1; k++) {
+ *(char *) (page + len) = (char) string_descriptor->wData[k];
+ len++;
+ }
+ len += sprintf ((char *) page + len, "\n");
+ break;
+ }
+ }
+ }
+ else
+ len += list_function(&usbd_simple_drivers, (char *)page + len, "Not Enabled");
+
+ break;
+
+ case 2:
+ len += list_function(&usbd_simple_drivers, (char *)page + len, "Simple Drivers");
+ break;
+ case 3:
+ len += list_function(&usbd_class_drivers, (char *)page + len, "Class Drivers");
+ break;
+ case 4:
+ len += list_function(&usbd_interface_drivers, (char *)page + len, "Interface Drivers");
+ break;
+
+ case 5:
+ if (otg_instance && otg_instance->function_name && strlen(otg_instance->function_name))
+ len += sprintf((char *)page + len, "\nActive: %s\n\n", otg_instance->function_name);
+ len += sprintf((char *)page + len, "\n%s\n", "Composite Drivers");
+ break;
+
+ default:
+ len += list_function_composite(&usbd_composite_drivers, (char *)page + len, index - 6);
+ break;
+ }
+
+ return len;
+}
+
+#if defined(CONFIG_OTG_LNX)
+/*! *
+ * usbd_device_proc_read_lnx - implement proc file system read.
+ * @param file
+ * @param buf
+ * @param count
+ * @param pos
+ *
+ * Standard proc file system read function.
+ *
+ * We let upper layers iterate for us, *pos will indicate which device to return
+ * statistics for.
+ */
+static ssize_t usbd_device_proc_read_lnx (struct file *file, char *buf, size_t count, loff_t * pos)
+{
+ unsigned long page;
+ int len = 0;
+ int index;
+
+ u8 config_descriptor[512];
+ int config_size;
+
+ if (!(page = GET_KERNEL_PAGE())) {
+ return -ENOMEM;
+ }
+
+ len = 0;
+ index = (*pos)++;
+
+ len = usbd_device_proc_read((char *)page, count, (int *)pos);
+
+ if (len > count) {
+ len = -EINVAL;
+ }
+ else if ((len > 0) && copy_to_user (buf, (char *) page, len)) {
+ len = -EFAULT;
+ }
+ free_page (page);
+ return len;
+}
+
+/* Module init ******************************************************************************* */
+
+/*!
+ * usbd_device_proc_operations_functions -
+ */
+static struct file_operations usbd_device_proc_operations_functions = {
+ read:usbd_device_proc_read_lnx,
+};
+#endif /* defined(CONFIG_OTG_LNX) */
+
+/*!
+ * usbd_procfs_init () -
+ */
+int usbd_procfs_init (struct usbd_bus_instance *bus)
+{
+ #if defined(CONFIG_OTG_LNX)
+ /* create proc filesystem entries */
+ struct proc_dir_entry *p;
+ RETURN_ENOMEM_UNLESS ((p = create_proc_entry ("usb-functions", 0, NULL)));
+ p->proc_fops = &usbd_device_proc_operations_functions;
+ #endif /* defined(CONFIG_OTG_LNX) */
+ procfs_usbd_bus_instance = bus;
+ return 0;
+}
+
+/*!
+ * usbd_procfs_exit () -
+ */
+void usbd_procfs_exit (struct usbd_bus_instance *bus)
+{
+ // remove proc filesystem entry
+ procfs_usbd_bus_instance = NULL;
+ #if defined(CONFIG_OTG_LNX)
+ remove_proc_entry ("usb-functions", NULL);
+ #endif /* defined(CONFIG_OTG_LNX) */
+}
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index 519b4ff79f7f..b6dacf89781c 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -269,6 +269,14 @@ 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 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 6f6478ba7174..8509609b3ba2 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o
obj-$(CONFIG_OMAP_CF) += omap_cf.o
obj-$(CONFIG_AT91_CF) += at91_cf.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..d8d305b5001c
--- /dev/null
+++ b/drivers/pcmcia/mx31ads-pcmcia.c
@@ -0,0 +1,1295 @@
+/*======================================================================
+ drivers/pcmcia/mx31ads-pcmica.c
+
+ Copyright 2005-2007 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/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <asm/mach-types.h>
+#include <asm/arch/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 <asm/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 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 = kmalloc(sizeof(struct mx31ads_pcmcia_socket), GFP_KERNEL);
+ if (!skt) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memset(skt, 0, sizeof(struct mx31ads_pcmcia_socket));
+
+ /*
+ * 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->dev = &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;
+ set_irq_type(skt->irq, IRQT_NOEDGE);
+
+ 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..50b74868bd1e
--- /dev/null
+++ b/drivers/pcmcia/mx31ads-pcmcia.h
@@ -0,0 +1,157 @@
+/*
+ * linux/drivers/pcmcia/mx31ads-pcmcia.h
+ *
+ * Copyright 2005-2007 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/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/ss.h>
+#include <pcmcia/bulkmem.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/rtc/Kconfig b/drivers/rtc/Kconfig
index 1e6715ec51ef..4f9e43b3f9ac 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -461,4 +461,11 @@ config RTC_DRV_RS5C313
help
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
+config RTC_MXC
+ tristate "Freescale MXC Real Time Clock"
+ depends on ARCH_MXC
+ depends on RTC_CLASS
+ help
+ Support for Freescale RTC MXC
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 465db4dd50b2..7249dfdb6974 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
new file mode 100644
index 000000000000..c3997d76a0db
--- /dev/null
+++ b/drivers/rtc/rtc-mxc.c
@@ -0,0 +1,815 @@
+/*
+ * 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
+ */
+/*
+ * 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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/uaccess.h>
+
+#include <asm/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)
+#include <asm/arch/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_time temp_time;
+ struct rtc_device *rtc;
+ struct rtc_plat_data *pdata = NULL;
+ u32 sec, reg;
+ int ret;
+
+ 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);
+ ret = get_ext_rtc_time(&sec);
+ if (ret == MXC_EXTERNAL_RTC_OK) {
+ rtc_time_to_tm(sec, &temp_time);
+ mxc_rtc_set_time(&pdev->dev, &temp_time);
+
+ } else if (ret == MXC_EXTERNAL_RTC_NONE) {
+ pr_info("No external RTC clock\n");
+ } else {
+ pr_info("Reading external RTC failed\n");
+ }
+ 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);
+
+}
+
+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/serial/8250.c b/drivers/serial/8250.c
index f94109cbb46e..717860873fa7 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -850,6 +850,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);
@@ -863,6 +867,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.
@@ -1374,7 +1379,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 d7e1996e2fec..2f53e7417f45 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -307,6 +307,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
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index af6377d480d7..122f32d77e71 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -62,5 +62,7 @@ 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
diff --git a/drivers/serial/mxc_uart.c b/drivers/serial/mxc_uart.c
new file mode 100644
index 000000000000..642a5603c5c9
--- /dev/null
+++ b/drivers/serial/mxc_uart.c
@@ -0,0 +1,1942 @@
+/*
+ * 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 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/arch/hardware.h>
+#include <asm/arch/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)
+{
+ struct tty_struct *tty = umxc->port.info->tty;
+ 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(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->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->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)
+{
+ int d = 1;
+
+ d = per_clk / ((req_baud * 16) + 1000);
+ if (d > 6) {
+ d = 6;
+ }
+
+ 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->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 */
+ num = (baud / 100) - 1;
+ denom = (umxc->port.uartclk / 1600) - 1;
+ if ((denom < 65536) && (umxc->port.uartclk > 1600)) {
+ 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->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 */
+ }
+ uart_suspend_port(&mxc_reg, &umxc->port);
+ if (umxc->port.info && umxc->port.info->flags & UIF_INITIALIZED) {
+ umxc->port.info->tty->hw_stopped = 1;
+ }
+
+ if (device_may_wakeup(&pdev->dev)) {
+ /* UART RTS signal is used as wakeup source */
+ writel(MXC_UARTUCR1_RTSDEN, umxc->port.membase + MXC_UARTUCR1);
+ gpio_uart_active(umxc->port.line, umxc->ir_mode);
+ }
+
+ 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_INITIALIZED) {
+ umxc->port.info->tty->hw_stopped = 0;
+ }
+ if (device_may_wakeup(&pdev->dev)) {
+ writel(0, umxc->port.membase + MXC_UARTUCR1);
+ gpio_uart_inactive(umxc->port.line, umxc->ir_mode);
+ }
+ 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");
+ clk_enable(mxc_ports[id]->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]);
+ pdev->dev.power.can_wakeup = 1;
+ }
+ 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..275f3a08b2ed
--- /dev/null
+++ b/drivers/serial/mxc_uart_early.c
@@ -0,0 +1,252 @@
+/*
+ * 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 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/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/clk.h>
+#include <asm/arch/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%lx (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/spi/Kconfig b/drivers/spi/Kconfig
index abf05048c638..13d10aa34cb2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -191,6 +191,33 @@ config SPI_XILINX
See the "OPB Serial Peripheral Interface (SPI) (v1.00e)"
Product Specification document (DS464) for hardware details.
+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
+ 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)
+ default n
+
#
# Add new SPI master controllers in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 41fbac45c323..6ed524fea90e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -26,6 +26,7 @@ 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
# ... add above this line ...
diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c
new file mode 100644
index 000000000000..54ffc54c10f1
--- /dev/null
+++ b/drivers/spi/mxc_spi.c
@@ -0,0 +1,913 @@
+/*
+ * 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-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/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 <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+
+#ifdef CONFIG_ARCH_MX27
+#include "mxc_spi_mx27.h"
+#else
+#include "mxc_spi.h"
+#endif
+
+#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 = "loopback_spi",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 1,
+ .chip_select = 4,
+ },
+#endif
+#ifdef CONFIG_SPI_MXC_SELECT2
+ {
+ .modalias = "loopback_spi",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 2,
+ .chip_select = 4,
+ },
+#endif
+#ifdef CONFIG_SPI_MXC_SELECT3
+ {
+ .modalias = "loopback_spi",
+ .controller_data = &lb_chip_info,
+ .irq = 0,
+ .max_speed_hz = 4000000,
+ .bus_num = 3,
+ .chip_select = 4,
+ },
+#endif
+};
+#endif
+
+extern void gpio_spi_active(int cspi_mod);
+extern void gpio_spi_inactive(int cspi_mod);
+
+static struct mxc_spi_unique_def spi_ver_0_7 = {
+ .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_7,
+ .cs_shift = MXC_CSPICTRL_CSSHIFT_0_7,
+ .bc_shift = MXC_CSPICTRL_BCSHIFT_0_7,
+ .bc_mask = MXC_CSPICTRL_BCMASK_0_7,
+ .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_7,
+ .xfer_complete = MXC_CSPISTAT_TC_0_7,
+ .bc_overflow = MXC_CSPISTAT_BO_0_7,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_5 = {
+ .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_5,
+ .cs_shift = MXC_CSPICTRL_CSSHIFT_0_5,
+ .bc_shift = MXC_CSPICTRL_BCSHIFT_0_5,
+ .bc_mask = MXC_CSPICTRL_BCMASK_0_5,
+ .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_5,
+ .xfer_complete = MXC_CSPISTAT_TC_0_5,
+ .bc_overflow = MXC_CSPISTAT_BO_0_5,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_4 = {
+ .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_4,
+ .cs_shift = MXC_CSPICTRL_CSSHIFT_0_4,
+ .bc_shift = MXC_CSPICTRL_BCSHIFT_0_4,
+ .bc_mask = MXC_CSPICTRL_BCMASK_0_4,
+ .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_4,
+ .xfer_complete = MXC_CSPISTAT_TC_0_4,
+ .bc_overflow = MXC_CSPISTAT_BO_0_4,
+};
+
+static struct mxc_spi_unique_def spi_ver_0_0 = {
+ .intr_bit_shift = MXC_CSPIINT_IRQSHIFT_0_0,
+ .cs_shift = MXC_CSPICTRL_CSSHIFT_0_0,
+ .bc_shift = MXC_CSPICTRL_BCSHIFT_0_0,
+ .bc_mask = MXC_CSPICTRL_BCMASK_0_0,
+ .drctrl_shift = MXC_CSPICTRL_DRCTRLSHIFT_0_0,
+ .xfer_complete = MXC_CSPISTAT_TC_0_0,
+ .bc_overflow = MXC_CSPISTAT_BO_0_0,
+};
+
+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;
+
+ /*!
+ * 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;
+};
+
+#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(master_data->base + MXC_CSPIINT)),
+ master_data->base + MXC_CSPIINT);
+
+ 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(master_data->base + MXC_CSPIINT)),
+ master_data->base + MXC_CSPIINT);
+ 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++;
+ MXC_CSPICTRL_ADJUST_SHIFT(shift);
+ if (shift > MXC_CSPICTRL_MAXDATRATE)
+ shift = MXC_CSPICTRL_MAXDATRATE;
+
+ return (shift << MXC_CSPICTRL_DATASHIFT);
+}
+
+/*!
+ * This function gets the received data.
+ *
+ * @param base the CSPI base address
+ *
+ * @return This function returns Rx FIFO data read.
+ */
+static unsigned int spi_get_rx_data(void *base)
+{
+ return __raw_readl(base + MXC_CSPIRXDATA);
+}
+
+/*!
+ * This function loads the transmit fifo.
+ *
+ * @param base the CSPI base address
+ * @param val the data to put in the TxFIFO
+ */
+static void spi_put_tx_data(void *base, unsigned int val)
+{
+ unsigned int ctrl_reg;
+
+ __raw_writel(val, base + MXC_CSPITXDATA);
+
+ ctrl_reg = __raw_readl(base + MXC_CSPICTRL);
+
+ ctrl_reg |= MXC_CSPICTRL_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;
+ unsigned int ctrl_mask;
+ unsigned int xfer_len;
+
+ 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);
+ spi_ver_def = master_drv_data->spi_ver_def;
+
+ xfer_len = spi->bits_per_word;
+
+ /* Control Register Settings for transfer to this slave */
+
+ ctrl_reg = __raw_readl(master_drv_data->base + MXC_CSPICTRL);
+
+ ctrl_mask =
+ (MXC_CSPICTRL_LOWPOL | MXC_CSPICTRL_PHA | MXC_CSPICTRL_HIGHSSPOL |
+ MXC_CSPICTRL_CSMASK << spi_ver_def->cs_shift |
+ MXC_CSPICTRL_DATAMASK << MXC_CSPICTRL_DATASHIFT |
+ spi_ver_def->bc_mask << spi_ver_def->bc_shift);
+ ctrl_reg &= ~ctrl_mask;
+
+ ctrl_reg |=
+ ((spi->chip_select & MXC_CSPICTRL_CSMASK) << spi_ver_def->cs_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 |= MXC_CSPICTRL_PHA;
+ if (!(spi->mode & SPI_CPOL))
+ ctrl_reg |= MXC_CSPICTRL_LOWPOL;
+ if (spi->mode & SPI_CS_HIGH)
+ ctrl_reg |= MXC_CSPICTRL_HIGHSSPOL;
+
+ __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 if (xfer_len <= 32) {
+ 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->base + MXC_CSPITEST);
+ else if (lb_chip->lb_enable)
+ __raw_writel(MXC_CSPITEST_LBC,
+ master_drv_data->base + MXC_CSPITEST);
+ }
+#endif
+ 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;
+
+ /* Read the interrupt status register to determine the source */
+ status = __raw_readl(master_drv_data->base + MXC_CSPISTAT);
+
+ /* Rx is given higher priority - Handle it first */
+ if (status & MXC_CSPISTAT_RR) {
+ u32 rx_tmp = spi_get_rx_data(master_drv_data->base);
+
+ if (master_drv_data->transfer.rx_buf)
+ master_drv_data->transfer.rx_get(master_drv_data,
+ rx_tmp);
+
+ ret = IRQ_HANDLED;
+ }
+
+ (master_drv_data->transfer.count)--;
+ /* Handle the Tx now */
+ if (master_drv_data->transfer.count) {
+ if (master_drv_data->transfer.tx_buf) {
+ u32 tx_tmp =
+ master_drv_data->transfer.tx_get(master_drv_data);
+
+ spi_put_tx_data(master_drv_data->base, tx_tmp);
+ }
+ } else {
+ complete(&master_drv_data->xfer_done);
+ }
+
+ /* Clear the interrupt status */
+ //__raw_writel(spi_ver_def->spi_status_transfer_complete,
+ // master_drv_data->base + MXC_CSPISTAT);
+
+ return ret;
+}
+
+/*!
+ * This function initialize the current SPI device.
+ *
+ * @param spi the current SPI device.
+ *
+ */
+int mxc_spi_setup(struct spi_device *spi)
+{
+ struct mxc_spi *master_data = spi_master_get_devdata(spi->master);
+
+ if ((spi->max_speed_hz < 0)
+ || (spi->max_speed_hz > (master_data->spi_ipg_clk / 4)))
+ 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. 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;
+
+ /* Get the master controller driver data from spi device's master */
+
+ master_drv_data = spi_master_get_devdata(spi->master);
+
+ /* 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;
+ INIT_COMPLETION(master_drv_data->xfer_done);
+
+ /* Enable the Rx Interrupts */
+
+ spi_enable_interrupt(master_drv_data, MXC_CSPIINT_RREN);
+
+ /* Perform single Tx transaction */
+
+ spi_put_tx_data(master_drv_data->base,
+ master_drv_data->transfer.tx_get(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, MXC_CSPIINT_RREN);
+
+ 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;
+
+ /* 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);
+
+ /* 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) {
+ 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);
+
+ /* 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;
+ }
+
+ dev_dbg(&pdev->dev, "SPI_REV 0.%d\n", spi_ver);
+
+ /* Enable the CSPI Clock, CSPI Module, set as a master */
+
+ 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(MXC_CSPIRESET_START,
+ master_drv_data->base + MXC_CSPIRESET);
+ udelay(1);
+ __raw_writel(MXC_CSPICTRL_ENABLE | MXC_CSPICTRL_MASTER,
+ master_drv_data->base + MXC_CSPICTRL);
+ __raw_writel(MXC_CSPIPERIOD_32KHZ,
+ master_drv_data->base + MXC_CSPIPERIOD);
+ __raw_writel(0, master_drv_data->base + MXC_CSPIINT);
+
+ /* 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
+ 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);
+ clk_disable(master_drv_data->clk);
+
+ /* Disable the CSPI module */
+
+ __raw_writel(MXC_CSPICTRL_DISABLE,
+ master_drv_data->base + MXC_CSPICTRL);
+
+ /* 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 suspend_devices(struct device *dev, void *pm_message)
+{
+ pm_message_t *state = pm_message;
+
+ if (dev->power.power_state.event != state->event) {
+ dev_warn(dev, "mismatch in pm state request\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+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->cdev.dev, "wait for queue\n");
+ msleep(10);
+
+ spin_lock_irqsave(&bitbang->lock, flags);
+ }
+ if (!list_empty(&bitbang->queue)) {
+ dev_err(bitbang->master->cdev.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;
+
+ if (device_for_each_child(&pdev->dev, &state, suspend_devices) != 0) {
+ dev_warn(&pdev->dev, "suspend aborted\n");
+ return -EINVAL;
+ }
+
+ spi_bitbang_suspend(&master_drv_data->mxc_bitbang);
+ __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);
+ clk_enable(master_drv_data->clk);
+
+ spi_bitbang_resume(&master_drv_data->mxc_bitbang);
+ __raw_writel(MXC_CSPICTRL_ENABLE | MXC_CSPICTRL_MASTER,
+ master_drv_data->base + MXC_CSPICTRL);
+
+ 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 = mxc_spi_suspend,
+ .resume = 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/mxc_spi.h b/drivers/spi/mxc_spi.h
new file mode 100644
index 000000000000..42b177493f79
--- /dev/null
+++ b/drivers/spi/mxc_spi.h
@@ -0,0 +1,153 @@
+/*
+ * 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_spi.h
+ * @brief This header file contains SPI driver low level register definitions.
+ *
+ * @ingroup SPI
+ */
+
+#ifndef __MXC_SPI_H__
+#define __MXC_SPI_H__
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#define MXC_CSPIRXDATA 0x00
+#define MXC_CSPITXDATA 0x04
+#define MXC_CSPICTRL 0x08
+#define MXC_CSPIINT 0x0C
+#define MXC_CSPIDMA 0x10
+#define MXC_CSPISTAT 0x14
+#define MXC_CSPIPERIOD 0x18
+#define MXC_CSPITEST 0x1C
+#define MXC_CSPIRESET 0x00
+
+#define MXC_CSPICTRL_ENABLE 0x1
+#define MXC_CSPICTRL_DISABLE 0x0
+#define MXC_CSPICTRL_MASTER (1 << 1)
+#define MXC_CSPICTRL_SLAVE 0x0
+#define MXC_CSPICTRL_XCH (1 << 2)
+#define MXC_CSPICTRL_SMC (1 << 3)
+#define MXC_CSPICTRL_LOWPOL (1 << 4)
+#define MXC_CSPICTRL_HIGHPOL 0x0
+#define MXC_CSPICTRL_PHA (1 << 5)
+#define MXC_CSPICTRL_NOPHA 0x0
+#define MXC_CSPICTRL_SSCTL (1 << 6)
+#define MXC_CSPICTRL_HIGHSSPOL (1 << 7)
+#define MXC_CSPICTRL_LOWSSPOL 0x0
+#define MXC_CSPICTRL_CSMASK 0x3
+#define MXC_CSPICTRL_MAXDATRATE 0x7
+#define MXC_CSPICTRL_DATAMASK 0x7
+#define MXC_CSPICTRL_DATASHIFT 16
+#define MXC_CSPICTRL_ADJUST_SHIFT(x) ((x) -= 2)
+
+#define MXC_CSPICTRL_CSSHIFT_0_7 12
+#define MXC_CSPICTRL_BCSHIFT_0_7 20
+#define MXC_CSPICTRL_BCMASK_0_7 0xFFF
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_7 8
+
+#define MXC_CSPICTRL_CSSHIFT_0_5 12
+#define MXC_CSPICTRL_BCSHIFT_0_5 20
+#define MXC_CSPICTRL_BCMASK_0_5 0xFFF
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_5 8
+
+#define MXC_CSPICTRL_CSSHIFT_0_4 24
+#define MXC_CSPICTRL_BCSHIFT_0_4 8
+#define MXC_CSPICTRL_BCMASK_0_4 0x1F
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_4 20
+
+#define MXC_CSPICTRL_CSSHIFT_0_0 19
+#define MXC_CSPICTRL_BCSHIFT_0_0 0
+#define MXC_CSPICTRL_BCMASK_0_0 0x1F
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_0 12
+
+#define MXC_CSPIINT_IRQSHIFT_0_7 8
+#define MXC_CSPIINT_IRQSHIFT_0_5 9
+#define MXC_CSPIINT_IRQSHIFT_0_4 9
+#define MXC_CSPIINT_IRQSHIFT_0_0 18
+#define MXC_CSPIINT_TEEN (1 << 0)
+#define MXC_CSPIINT_THEN (1 << 1)
+#define MXC_CSPIINT_TFEN (1 << 2)
+#define MXC_CSPIINT_RREN (1 << 3)
+#define MXC_CSPIINT_RHEN (1 << 4)
+#define MXC_CSPIINT_RFEN (1 << 5)
+#define MXC_CSPIINT_ROEN (1 << 6)
+#define MXC_CSPIINT_TCEN_0_7 (1 << 7)
+#define MXC_CSPIINT_TCEN_0_5 (1 << 8)
+#define MXC_CSPIINT_TCEN_0_4 (1 << 8)
+#define MXC_CSPIINT_TCEN_0_0 (1 << 3)
+#define MXC_CSPIINT_BOEN_0_7 0
+#define MXC_CSPIINT_BOEN_0_5 (1 << 7)
+#define MXC_CSPIINT_BOEN_0_4 (1 << 7)
+#define MXC_CSPIINT_BOEN_0_0 (1 << 8)
+
+#define MXC_CSPISTAT_TE (1 << 0)
+#define MXC_CSPISTAT_TH (1 << 1)
+#define MXC_CSPISTAT_TF (1 << 2)
+#define MXC_CSPISTAT_RR (1 << 3)
+#define MXC_CSPISTAT_RH (1 << 4)
+#define MXC_CSPISTAT_RF (1 << 5)
+#define MXC_CSPISTAT_RO (1 << 6)
+#define MXC_CSPISTAT_TC_0_7 (1 << 7)
+#define MXC_CSPISTAT_TC_0_5 (1 << 8)
+#define MXC_CSPISTAT_TC_0_4 (1 << 8)
+#define MXC_CSPISTAT_TC_0_0 (1 << 3)
+#define MXC_CSPISTAT_BO_0_7 0
+#define MXC_CSPISTAT_BO_0_5 (1 << 7)
+#define MXC_CSPISTAT_BO_0_4 (1 << 7)
+#define MXC_CSPISTAT_BO_0_0 (1 << 8)
+
+#define MXC_CSPIPERIOD_32KHZ (1 << 15)
+
+#define MXC_CSPITEST_LBC (1 << 14)
+
+#define MXC_CSPIRESET_START 1
+
+/*!
+ * @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;
+};
+#endif //__MXC_SPI_H__
diff --git a/drivers/spi/mxc_spi_mx27.h b/drivers/spi/mxc_spi_mx27.h
new file mode 100644
index 000000000000..06921f3dacd9
--- /dev/null
+++ b/drivers/spi/mxc_spi_mx27.h
@@ -0,0 +1,155 @@
+/*
+ * 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_spi_mx27.h
+ * @brief This header file contains SPI driver low level register definitions for MX27.
+ *
+ * @ingroup SPI
+ */
+
+#ifndef __MXC_SPI_MX27_H__
+#define __MXC_SPI_MX27_H__
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+
+#define MXC_CSPIRXDATA 0x00
+#define MXC_CSPITXDATA 0x04
+#define MXC_CSPICTRL 0x08
+#define MXC_CSPIINT 0x0C
+#define MXC_CSPIDMA 0x18
+#define MXC_CSPISTAT 0x0C
+#define MXC_CSPIPERIOD 0x14
+#define MXC_CSPITEST 0x10
+#define MXC_CSPIRESET 0x1C
+
+#define MXC_CSPICTRL_ENABLE (1 << 10)
+#define MXC_CSPICTRL_DISABLE 0x0
+#define MXC_CSPICTRL_MASTER (1 << 11)
+#define MXC_CSPICTRL_SLAVE 0x0
+#define MXC_CSPICTRL_XCH (1 << 9)
+#define MXC_CSPICTRL_LOWPOL (1 << 5)
+#define MXC_CSPICTRL_HIGHPOL 0x0
+#define MXC_CSPICTRL_PHA (1 << 6)
+#define MXC_CSPICTRL_NOPHA 0x0
+#define MXC_CSPICTRL_SSCTL (1 << 7)
+#define MXC_CSPICTRL_HIGHSSPOL (1 << 8)
+#define MXC_CSPICTRL_LOWSSPOL 0x0
+#define MXC_CSPICTRL_CSMASK 0x3
+#define MXC_CSPICTRL_MAXDATRATE 0x10
+#define MXC_CSPICTRL_DATAMASK 0x1F
+#define MXC_CSPICTRL_DATASHIFT 14
+/* This adjustment in the shift is valid only for even states only(i.e. divide
+ ratio of 2). SDHC_SPIEN is not set by default. If SDHC_SPIEN bit is set in
+ MXC_CSPICTRL, then divide ratio is 3, this shift adjustment is invalid. */
+#define MXC_CSPICTRL_ADJUST_SHIFT(x) ((x) = ((x) - 1) * 2)
+
+#define MXC_CSPICTRL_CSSHIFT_0_7 12
+#define MXC_CSPICTRL_BCSHIFT_0_7 20
+#define MXC_CSPICTRL_BCMASK_0_7 0xFFF
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_7 8
+
+#define MXC_CSPICTRL_CSSHIFT_0_5 12
+#define MXC_CSPICTRL_BCSHIFT_0_5 20
+#define MXC_CSPICTRL_BCMASK_0_5 0xFFF
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_5 8
+
+#define MXC_CSPICTRL_CSSHIFT_0_4 24
+#define MXC_CSPICTRL_BCSHIFT_0_4 8
+#define MXC_CSPICTRL_BCMASK_0_4 0x1F
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_4 20
+
+#define MXC_CSPICTRL_CSSHIFT_0_0 19
+#define MXC_CSPICTRL_BCSHIFT_0_0 0
+#define MXC_CSPICTRL_BCMASK_0_0 0x1F
+#define MXC_CSPICTRL_DRCTRLSHIFT_0_0 12
+
+#define MXC_CSPIINT_IRQSHIFT_0_7 8
+#define MXC_CSPIINT_IRQSHIFT_0_5 9
+#define MXC_CSPIINT_IRQSHIFT_0_4 9
+#define MXC_CSPIINT_IRQSHIFT_0_0 18
+#define MXC_CSPIINT_TEEN (1 << 9)
+#define MXC_CSPIINT_THEN (1 << 10)
+#define MXC_CSPIINT_TFEN (1 << 11)
+#define MXC_CSPIINT_RREN (1 << 13)
+#define MXC_CSPIINT_RHEN (1 << 14)
+#define MXC_CSPIINT_RFEN (1 << 15)
+#define MXC_CSPIINT_ROEN (1 << 16)
+#define MXC_CSPIINT_TCEN_0_7 (1 << 7)
+#define MXC_CSPIINT_TCEN_0_5 (1 << 8)
+#define MXC_CSPIINT_TCEN_0_4 (1 << 8)
+#define MXC_CSPIINT_TCEN_0_0 (1 << 12)
+#define MXC_CSPIINT_BOEN_0_7 0
+#define MXC_CSPIINT_BOEN_0_5 (1 << 7)
+#define MXC_CSPIINT_BOEN_0_4 (1 << 7)
+#define MXC_CSPIINT_BOEN_0_0 (1 << 17)
+
+#define MXC_CSPISTAT_TE (1 << 0)
+#define MXC_CSPISTAT_TH (1 << 1)
+#define MXC_CSPISTAT_TF (1 << 2)
+#define MXC_CSPISTAT_RR (1 << 3)
+#define MXC_CSPISTAT_RH (1 << 4)
+#define MXC_CSPISTAT_RF (1 << 5)
+#define MXC_CSPISTAT_RO (1 << 6)
+#define MXC_CSPISTAT_TC_0_7 (1 << 7)
+#define MXC_CSPISTAT_TC_0_5 (1 << 8)
+#define MXC_CSPISTAT_TC_0_4 (1 << 8)
+#define MXC_CSPISTAT_TC_0_0 (1 << 3)
+#define MXC_CSPISTAT_BO_0_7 0
+#define MXC_CSPISTAT_BO_0_5 (1 << 7)
+#define MXC_CSPISTAT_BO_0_4 (1 << 7)
+#define MXC_CSPISTAT_BO_0_0 (1 << 8)
+
+#define MXC_CSPIPERIOD_32KHZ (1 << 15)
+
+#define MXC_CSPITEST_LBC (1 << 14)
+
+#define MXC_CSPIRESET_START 1
+
+/*!
+ * @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;
+};
+#endif //__MXC_SPI_MX27_H__
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 7580aa5da0f8..a072eafb5509 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -89,6 +89,8 @@ config USB
source "drivers/usb/core/Kconfig"
+source "drivers/usb/otg/Kconfig"
+
source "drivers/usb/host/Kconfig"
source "drivers/usb/class/Kconfig"
@@ -99,6 +101,8 @@ source "drivers/usb/image/Kconfig"
source "drivers/usb/mon/Kconfig"
+source "drivers/usb/usblan/Kconfig"
+
comment "USB port drivers"
depends on USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 516a6400db43..f9ba676e1733 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -32,3 +32,6 @@ obj-$(CONFIG_USB) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
+
+obj-$(CONFIG_USB_OTG) += otg/
+obj-$(CONFIG_USB_USBLAN) += usblan/
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 97b09f282705..77af764e7904 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -112,11 +112,14 @@ config USB_PERSIST
If you are unsure, say N here.
config USB_OTG
- bool
+ bool "Enable host-side support for On-The-Go (OTG)"
depends on USB && EXPERIMENTAL
select USB_SUSPEND
default n
-
+ help
+ Say y here if you want support for the Host Negotiation Protocol
+ (HNP) and the Session Request Protocol (SRP), parts of the OTG
+ supplement to the USB protocol, to be included in the USB host core.
config USB_OTG_WHITELIST
bool "Rely on OTG Targeted Peripherals List"
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b04d232d4c65..6fd435fff671 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -929,6 +929,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
+ /* 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.
+ */
+#ifdef CONFIG_USB_EHCI_ARC_OTG
+ hdev->autosuspend_disabled = 1;
+#endif
+
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f81d08d6538b..4f2ccf6cdbf9 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -274,16 +274,25 @@ 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
+config USB_GADGET_ARC
+ boolean "ARC USB Device Controller"
+ depends on ARCH_MXC
+ select USB_GADGET_DUALSPEED
+ select USB_GADGET_ARC_OTGHS if USB_EHCI_ARC_OTGHS
+ select USB_GADGET_ARC_OTGFS if USB_EHCI_ARC_OTGFS
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.
+ Some Freescale processors have an ARC High Speed
+ USBOTG controller, which supports device mode.
- Select this only if your OMAP board has a Mini-AB connector.
+ 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_ARC
+ tristate
+ depends on USB_GADGET_ARC
+ default USB_GADGET
+ select USB_GADGET_SELECTED
config USB_GADGET_S3C2410
boolean "S3C2410 USB Device Controller"
@@ -358,6 +367,18 @@ config USB_DUMMY_HCD
endchoice
+config USB_OTG
+ boolean "OTG Support"
+ depends on (USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD) || \
+ (USB_GADGET_ARC && (ARCH_MX3 || ARCH_MX27 || ARCH_MXC91221) && 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 board has a Mini-AB connector.
+
config USB_GADGET_DUALSPEED
bool
depends on USB_GADGET
@@ -366,6 +387,38 @@ 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 OTG prepheral port on ARC controller"
+ depends on USB_GADGET_ARC
+ default y
+ ---help---
+ Enable support for the USB OTG port in HS/FS prepheral mode.
+
+choice
+ prompt "Select transceiver"
+ depends on USB_GADGET_ARC_OTG
+
+config USB_GADGET_ARC_OTGFS
+ bool "Full Speed"
+ depends on !USB_EHCI_ARC_OTGHS
+ help
+ The ARC OTG controller can be connected to either a FS or
+ a HS transceiver.
+
+ Enable this configuration option if you want to use the FS
+ transceiver.
+
+config USB_GADGET_ARC_OTGHS
+ bool "High Speed"
+ depends on !USB_EHCI_ARC_OTGFS
+ help
+ The ARC OTG controller can be connected to either a FS or
+ a HS transceiver.
+
+ Enable this configuration option if you want to use the HS
+ transceiver.
+endchoice
+
#
# USB Gadget Drivers
#
@@ -418,7 +471,7 @@ config USB_ZERO
config USB_ZERO_HNPTEST
boolean "HNP Test Device"
- depends on USB_ZERO && USB_OTG
+ depends on USB_ZERO && USB_OTG2
help
You can configure this device to enumerate using the device
identifiers of the USB-OTG test device. That means that when
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 904e57bf6112..ac95b08a7179 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -17,6 +17,7 @@ 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_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..9f365abdbae6
--- /dev/null
+++ b/drivers/usb/gadget/arcotg_udc.c
@@ -0,0 +1,3126 @@
+/*
+ * 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 arcotg_udc.c
+ * @brief arc otg device controller driver
+ * @ingroup USB
+ */
+
+/*!
+ * Include files
+ */
+
+#if 0
+#define DEBUG
+#define VERBOSE
+#define DUMP_QUEUES
+#endif
+
+#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/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <linux/usb/otg.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+#include "arcotg_udc.h"
+#include <asm/arch/arc_otg.h>
+
+extern void gpio_usbotg_hs_active(void);
+extern void gpio_usbotg_fs_active(void);
+
+static void Ep0Stall(struct arcotg_udc *);
+static int ep0_prime_status(struct arcotg_udc *, int);
+
+static int timeout;
+
+#undef USB_TRACE
+
+#define DRIVER_DESC "ARC USBOTG Device Controller driver"
+#define DRIVER_AUTHOR "Freescale Semiconductor"
+#define DRIVER_VERSION "1 August 2005"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+/*#define DEBUG_FORCE_FS 1 */
+
+static const char driver_name[] = "arc_udc";
+static const char driver_desc[] = DRIVER_DESC;
+
+volatile static struct usb_dr_device *usb_slave_regs;
+
+/* it is initialized in probe() */
+static struct arcotg_udc *udc_controller;
+
+/*ep name is important in gadget, it should obey the convention of ep_match()*/
+/* even numbered EPs are OUT or setup, odd are IN/INTERRUPT */
+static const char *const ep_name[] = {
+ "ep0-control", NULL, /* everyone has ep0 */
+ /* 7 configurable endpoints */
+ "ep1out",
+ "ep1in",
+ "ep2out",
+ "ep2in",
+ "ep3out",
+ "ep3in",
+ "ep4out",
+ "ep4in",
+ "ep5out",
+ "ep5in",
+ "ep6out",
+ "ep6in",
+ "ep7out",
+ "ep7in"
+};
+static const struct usb_endpoint_descriptor arcotg_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = 0,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
+};
+
+static int udc_suspend(struct arcotg_udc *udc);
+static int arcotg_udc_suspend(struct device *dev, pm_message_t state);
+static int arcotg_udc_resume(struct device *dev);
+
+/********************************************************************
+ * Internal Used Function
+********************************************************************/
+
+#ifdef DUMP_QUEUES
+static void dump_ep_queue(struct arcotg_ep *ep)
+{
+ int ep_index;
+ struct arcotg_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 arcotg_ep *ep)
+{
+}
+#endif
+
+/*!
+ * done() - retire a request; caller blocked irqs
+ * @param ep endpoint pointer
+ * @param req require pointer
+ * @param status when req->req.status is -EINPROGRESSS, it is input para
+ * else it will be a output parameter
+ * req->req.status : in ep_queue() it will be set as -EINPROGRESS
+ */
+static void done(struct arcotg_ep *ep, struct arcotg_req *req, int status)
+{
+ struct arcotg_udc *udc = NULL;
+ unsigned char stopped = ep->stopped;
+
+ udc = (struct arcotg_udc *)ep->udc;
+
+ pr_debug("udc: req=0x%p\n", req);
+ if (req->head) {
+ pr_debug("udc: freeing head=0x%p\n", req->head);
+ dma_pool_free(udc->dtd_pool, req->head, req->head->td_dma);
+ }
+
+ /* the req->queue pointer is used by ep_queue() func, in which
+ * the request will be added into a udc_ep->queue 'd tail
+ * so here the req will be dropped from the 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;
+
+ pr_debug("udc: req=0x%p mapped=%x\n", req, req->mapped);
+
+ if (req->mapped) {
+ pr_debug("udc: calling dma_unmap_single(buf,%s) req=0x%p "
+ "a=0x%x len=%d\n",
+ ep_is_in(ep) ? "to_dvc" : "from_dvc",
+ req, req->req.dma, req->req.length);
+
+ 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;
+ pr_debug("udc: req=0x%p set req.dma=0x%x\n", req, req->req.dma);
+ } else {
+ if ((req->req.length != 0)
+ && (req->req.dma != DMA_ADDR_INVALID)) {
+ pr_debug("udc: calling dma_sync_single_for_cpu(buf,%s) "
+ "req=0x%p dma=0x%x len=%d\n",
+ ep_is_in(ep) ? "to_dvc" : "from_dvc", req,
+ req->req.dma, req->req.length);
+
+ 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)) {
+ pr_debug("udc: complete %s req 0c%p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+ }
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+
+ spin_unlock(&ep->udc->lock);
+
+ /* this complete() should a func implemented by gadget layer,
+ * eg fsg->bulk_in_complete() */
+ if (req->req.complete) {
+ pr_debug("udc: calling gadget's complete() req=0x%p\n", req);
+ req->req.complete(&ep->ep, &req->req);
+ pr_debug("udc: back from gadget's complete()\n");
+ }
+
+ spin_lock(&ep->udc->lock);
+ ep->stopped = stopped;
+}
+
+/*!
+ * nuke(): delete all requests related to this ep
+ * called by ep_disable() within spinlock held
+ * add status paramter?
+ * @param ep endpoint pointer
+ * @param status current status
+ */
+static void nuke(struct arcotg_ep *ep, int status)
+{
+ pr_debug("udc: ep=0x%p\n", ep);
+ ep->stopped = 1;
+
+ /* Whether this eq has request linked */
+ while (!list_empty(&ep->queue)) {
+ struct arcotg_req *req = NULL;
+
+ req = list_entry(ep->queue.next, struct arcotg_req, queue);
+
+ done(ep, req, status);
+ }
+ dump_ep_queue(ep);
+}
+
+/*------------------------------------------------------------------
+ Internal Hardware related function
+ ------------------------------------------------------------------*/
+extern void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata,
+ int on);
+
+/*
+ * init device controller
+ * @param qh_addr the aligned virt addr of ep QH addr
+ * @param dev device controller pointer
+ */
+static int dr_controller_setup(struct arcotg_udc *udc)
+{
+ unsigned int tmp = 0, portctrl = 0;
+ void *qh_addr = udc->ep_qh;
+ struct device *dev __attribute((unused)) = udc->gadget.dev.parent;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = udc->pdata;
+
+ pr_debug("udc: dev=0x%p\n", dev);
+
+ /* before here, make sure usb_slave_regs has been initialized */
+ if (!qh_addr)
+ return -EINVAL;
+
+ /* Stop and reset the usb controller */
+ tmp = le32_to_cpu(usb_slave_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ usb_slave_regs->usbcmd = cpu_to_le32(tmp);
+
+ tmp = le32_to_cpu(usb_slave_regs->usbcmd);
+ tmp |= USB_CMD_CTRL_RESET;
+ usb_slave_regs->usbcmd = cpu_to_le32(tmp);
+
+ /* Wait for reset to complete */
+ timeout = 10000000;
+ while ((le32_to_cpu(usb_slave_regs->usbcmd) & USB_CMD_CTRL_RESET) &&
+ --timeout) {
+ continue;
+ }
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+ return -ETIMEDOUT;
+ }
+
+ /* Set the controller as device mode */
+ tmp = le32_to_cpu(usb_slave_regs->usbmode);
+ tmp |= USB_MODE_CTRL_MODE_DEVICE;
+ /* Disable Setup Lockout */
+ tmp |= USB_MODE_SETUP_LOCK_OFF;
+ usb_slave_regs->usbmode = cpu_to_le32(tmp);
+
+ if (pdata->xcvr_ops && pdata->xcvr_ops->set_device)
+ pdata->xcvr_ops->set_device();
+
+ /* Clear the setup status */
+ usb_slave_regs->usbsts = 0;
+
+ tmp = udc->ep_qh_dma;
+ tmp &= USB_EP_LIST_ADDRESS_MASK;
+ usb_slave_regs->endpointlistaddr = cpu_to_le32(tmp);
+
+ pr_debug("udc: vir[qh_base]=0x%p phy[qh_base]=0x%8x epla_reg=0x%8x\n",
+ qh_addr, (int)tmp,
+ le32_to_cpu(usb_slave_regs->endpointlistaddr));
+
+ portctrl = le32_to_cpu(usb_slave_regs->portsc1);
+ portctrl &= ~PORTSCX_PHY_TYPE_SEL;
+
+ portctrl |= udc->xcvr_type;
+
+#ifdef DEBUG_FORCE_FS
+ portctrl |= 0x1000000;
+#endif
+
+ usb_slave_regs->portsc1 = cpu_to_le32(portctrl);
+
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ return 0;
+}
+
+/*!
+ * just Enable DR irq reg and Set Dr controller Run
+ * @param udc device controller pointer
+ */
+
+static void dr_controller_run(struct arcotg_udc *udc)
+{
+ u32 tmp;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ /*Enable DR irq reg */
+ tmp = 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;
+
+ usb_slave_regs->usbintr = le32_to_cpu(tmp);
+
+ /* Clear stopped bit */
+ udc->stopped = 0;
+
+ /* Set the controller as device mode */
+ tmp = le32_to_cpu(usb_slave_regs->usbmode);
+ tmp |= USB_MODE_CTRL_MODE_DEVICE;
+ usb_slave_regs->usbmode = cpu_to_le32(tmp);
+
+ /* Set controller to Run */
+ tmp = le32_to_cpu(usb_slave_regs->usbcmd);
+ tmp |= USB_CMD_RUN_STOP;
+ usb_slave_regs->usbcmd = le32_to_cpu(tmp);
+
+ return;
+}
+
+/*
+ * just Disable DR irq reg and Set Dr controller Stop
+ * @param udc device controller pointer
+ */
+static void dr_controller_stop(struct arcotg_udc *udc)
+{
+ unsigned int tmp;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ /* 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 (!(usb_slave_regs->otgsc & OTGSC_STS_USB_ID)) {
+ pr_debug("udc: Leaving early\n");
+ return;
+ }
+ }
+
+ /* disable all INTR */
+ usb_slave_regs->usbintr = 0;
+
+ /* Set stopped bit for isr */
+ udc->stopped = 1;
+
+ /* set controller to Stop */
+ tmp = le32_to_cpu(usb_slave_regs->usbcmd);
+ tmp &= ~USB_CMD_RUN_STOP;
+ usb_slave_regs->usbcmd = le32_to_cpu(tmp);
+}
+
+void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
+{
+ unsigned int tmp_epctrl = 0;
+
+ tmp_epctrl = le32_to_cpu(usb_slave_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);
+ }
+
+ usb_slave_regs->endptctrl[ep_num] = cpu_to_le32(tmp_epctrl);
+
+ /* wait for the write reg to finish */
+ timeout = 10000000;
+ while ((!(le32_to_cpu(usb_slave_regs->endptctrl[ep_num]) &
+ (tmp_epctrl & (EPCTRL_TX_ENABLE | EPCTRL_RX_ENABLE))))
+ && --timeout) {
+ continue;
+ }
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+ }
+}
+
+static void dr_ep_change_stall(unsigned char ep_num, unsigned char dir,
+ int value)
+{
+ unsigned int tmp_epctrl = 0;
+
+ tmp_epctrl = le32_to_cpu(usb_slave_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;
+ }
+
+ }
+ usb_slave_regs->endptctrl[ep_num] = cpu_to_le32(tmp_epctrl);
+}
+
+/********************************************************************
+ Internal Structure Build up functions
+********************************************************************/
+
+/*!
+ * set the Endpoint Capabilites field of QH
+ * @param handle udc handler
+ * @param ep_num endpoint number
+ * @param dir in or out
+ * @param ep_type USB_ENDPOINT_XFER_CONTROL or other
+ * @param max_pkt_len max packet length of this endpoint
+ * @param zlt Zero Length Termination Select
+ * @param mult Mult field
+ */
+static void struct_ep_qh_setup(void *handle, unsigned char ep_num,
+ unsigned char dir, unsigned char ep_type,
+ unsigned int max_pkt_len,
+ unsigned int zlt, unsigned char mult)
+{
+ struct arcotg_udc *udc = NULL;
+ struct ep_queue_head *p_QH = NULL;
+ unsigned int tmp = 0;
+
+ udc = (struct arcotg_udc *)handle;
+
+ p_QH = &udc->ep_qh[2 * ep_num + dir];
+
+ /* set the Endpoint Capabilites Reg of 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;
+ if (zlt)
+ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
+ break;
+ default:
+ pr_debug("udc: error ep type is %d\n", ep_type);
+ return;
+ }
+ p_QH->max_pkt_length = le32_to_cpu(tmp);
+
+ return;
+}
+
+/*!
+ * This function only to make code looks good
+ * it is a collection of struct_ep_qh_setup and dr_ep_setup for ep0
+ * ep0 should set OK before the bind() of gadget layer
+ * @param udc device controller pointer
+ */
+static void ep0_dr_and_qh_setup(struct arcotg_udc *udc)
+{
+ /* the intialization of an ep includes: fields in QH, Regs,
+ * arcotg_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 will not use this func it is enable in probe()
+ * @param _ep endpoint pointer
+ * @param desc endpoint descriptor pointer
+ * @return The function returns 0 on success or -1 if failed
+ */
+static int arcotg_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct arcotg_udc *udc = NULL;
+ struct arcotg_ep *ep = NULL;
+ unsigned short max = 0;
+ unsigned char mult = 0, zlt = 0;
+ int retval = 0;
+ unsigned long flags = 0;
+ char *val = NULL; /* for debug */
+
+ ep = container_of(_ep, struct arcotg_ep, ep);
+
+ pr_debug("udc: %s ep.name=%s\n", __FUNCTION__, ep->ep.name);
+ /* catch various bogus parameters */
+ if (!_ep || !desc || ep->desc || _ep->name == ep_name[0] ||
+ (desc->bDescriptorType != USB_DT_ENDPOINT))
+ /* FIXME: add judge for ep->bEndpointAddress */
+ return -EINVAL;
+
+ udc = ep->udc;
+
+ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
+ return -ESHUTDOWN;
+
+ max = le16_to_cpu(desc->wMaxPacketSize);
+ retval = -EINVAL;
+
+ /* check the max package size validate for this endpoint */
+ /* Refer to USB2.0 spec table 9-13,
+ */
+ switch (desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_BULK:
+ if (strstr(ep->ep.name, "-iso")
+ || strstr(ep->ep.name, "-int"))
+ goto en_done;
+ mult = 0;
+ zlt = 1;
+ switch (udc->gadget.speed) {
+ case USB_SPEED_HIGH:
+ if ((max == 128) || (max == 256) || (max == 512))
+ break;
+ default:
+ switch (max) {
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ break;
+ default:
+ case USB_SPEED_LOW:
+ goto en_done;
+ }
+ }
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ if (strstr(ep->ep.name, "-iso")) /* bulk is ok */
+ goto en_done;
+ mult = 0;
+ zlt = 1;
+ switch (udc->gadget.speed) {
+ case USB_SPEED_HIGH:
+ if (max <= 1024)
+ break;
+ case USB_SPEED_FULL:
+ if (max <= 64)
+ break;
+ default:
+ if (max <= 8)
+ break;
+ goto en_done;
+ }
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (strstr(ep->ep.name, "-bulk") || strstr(ep->ep.name, "-int"))
+ goto en_done;
+ mult = (unsigned char)
+ (1 + ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 0x03));
+ zlt = 0;
+ switch (udc->gadget.speed) {
+ case USB_SPEED_HIGH:
+ if (max <= 1024)
+ break;
+ case USB_SPEED_FULL:
+ if (max <= 1023)
+ break;
+ default:
+ goto en_done;
+ }
+ break;
+ case USB_ENDPOINT_XFER_CONTROL:
+ if (strstr(ep->ep.name, "-iso") || strstr(ep->ep.name, "-int"))
+ goto en_done;
+ mult = 0;
+ zlt = 1;
+ switch (udc->gadget.speed) {
+ case USB_SPEED_HIGH:
+ case USB_SPEED_FULL:
+ switch (max) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ break;
+ default:
+ goto en_done;
+ }
+ case USB_SPEED_LOW:
+ switch (max) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ goto en_done;
+ }
+ default:
+ goto en_done;
+ }
+ break;
+
+ default:
+ goto en_done;
+ }
+
+ /* here initialize variable of ep */
+ spin_lock_irqsave(&udc->lock, flags);
+ ep->ep.maxpacket = max;
+ ep->desc = desc;
+ ep->stopped = 0;
+
+ /* hardware special operation */
+
+ /* Init EPx Queue Head (Ep Capabilites field in QH
+ * according to max, zlt, mult) */
+ struct_ep_qh_setup((void *)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 x at here */
+ /* 83xx RM chapter 16.3.2.24, here init the endpoint ctrl reg */
+ 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));
+
+ /* Now HW will be NAKing transfers to that EP,
+ * until a buffer is queued to it. */
+
+ /* should have stop the lock */
+ spin_unlock_irqrestore(&udc->lock, flags);
+ retval = 0;
+ switch (desc->bmAttributes & 0x03) {
+ case USB_ENDPOINT_XFER_BULK:
+ val = "bulk";
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ val = "iso";
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ val = "intr";
+ break;
+ default:
+ val = "ctrl";
+ break;
+ }
+
+ pr_debug("udc: enabled %s (ep%d%s-%s) maxpacket %d\n", ep->ep.name,
+ ep->desc->bEndpointAddress & 0x0f,
+ (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+ val, max);
+ en_done:
+ return retval;
+}
+
+/*!
+ * disable endpoint
+ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
+ * @param _ep the ep being unconfigured. May not be ep0
+ * @return The function returns 0 on success or -1 if failed
+ */
+static int arcotg_ep_disable(struct usb_ep *_ep)
+{
+ struct arcotg_udc *udc = NULL;
+ struct arcotg_ep *ep = NULL;
+ unsigned long flags = 0;
+
+ ep = container_of(_ep, struct arcotg_ep, ep);
+ if (!_ep || !ep->desc) {
+ pr_debug("udc: %s not enabled\n", _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+
+ udc = (struct arcotg_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);
+
+ pr_debug("udc: disabled %s OK\n", _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
+ * @param _ep the ep being unconfigured. May not be ep0
+ * @param gfp_flags mem flags
+ * @return Returns the request, or null if one could not be allocated
+ */
+static struct usb_request *arcotg_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct arcotg_req *req = NULL;
+
+ req = kmalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return NULL;
+
+ memset(req, 0, sizeof *req);
+ 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;
+}
+
+/*!
+ * free request memory
+ * @param _ep the ep being unconfigured. May not be ep0
+ * @param _req usb request pointer
+ */
+static void arcotg_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct arcotg_req *req;
+
+ req = container_of(_req, struct arcotg_req, req);
+ pr_debug("udc: req=0x%p\n", req);
+
+ if (_req)
+ kfree(req);
+}
+
+/*!
+ * Allocate an I/O buffer for the ep->req->buf.
+ * @param _ep endpoint pointer
+ * @param bytes length of the desired buffer
+ * @param dma pointer to the buffer's DMA address; must be valid
+ * when gadget layer calls this function, ma is &req->dma
+ * @param gfp_flags GFP_* flags to use
+ * @return Returns a new buffer, or null if one could not be allocated
+ */
+static void *arcotg_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t * dma, gfp_t gfp_flags)
+{
+ void *retval = NULL;
+
+ if (!bytes)
+ return 0;
+
+ retval = kmalloc(bytes, gfp_flags);
+
+ if (retval)
+ *dma = virt_to_phys(retval);
+
+ pr_debug("udc: ep=%s buffer=0x%p dma=0x%x len=%d\n",
+ _ep->name, retval, *dma, bytes);
+
+ return retval;
+}
+
+/*!
+ * Free an I/O buffer for the ep->req->buf
+ * @param _ep endpoint pointer
+ * @param buf data buf pointer
+ * @param dma for 834x, we will not touch dma field
+ * @param bytes buffer size
+ */
+static void arcotg_free_buffer(struct usb_ep *_ep, void *buf,
+ dma_addr_t dma, unsigned bytes)
+{
+ pr_debug("udc: buf=0x%p dma=0x%x\n", buf, dma);
+ if (buf)
+ kfree(buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * link req's dTD queue to the end of ep's QH's dTD queue.
+ * @param ep endpoint pointer
+ * @param req request pointer
+ * @return The function returns 0 on success or -1 if failed
+ */
+static int arcotg_queue_td(struct arcotg_ep *ep, struct arcotg_req *req)
+{
+ int i = ep_index(ep) * 2 + ep_is_in(ep);
+ u32 temp, bitmask, tmp_stat;
+ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
+
+ pr_debug("udc: queue req=0x%p to ep index %d\n", req, 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 arcotg_req *lastreq;
+ lastreq = list_entry(ep->queue.prev, struct arcotg_req, queue);
+
+ WARN_ON(req->head->td_dma & 31);
+ lastreq->tail->next_td_ptr = req->head->td_dma;
+ lastreq->tail->next_td_virt = req->head;
+
+ pr_debug("udc: ep's queue not empty. lastreq=0x%p\n", lastreq);
+
+ /* Read prime bit, if 1 goto done */
+ if (usb_slave_regs->endpointprime & cpu_to_le32(bitmask)) {
+ pr_debug("udc: ep's already primed\n");
+ goto out;
+ }
+
+ timeout = 10000000;
+ do {
+ /* Set ATDTW bit in USBCMD */
+ usb_slave_regs->usbcmd |= cpu_to_le32(USB_CMD_ATDTW);
+
+ /* Read correct status bit */
+ tmp_stat = le32_to_cpu(usb_slave_regs->endptstatus) &
+ bitmask;
+
+ } while ((!(usb_slave_regs->usbcmd &
+ cpu_to_le32(USB_CMD_ATDTW)))
+ && --timeout);
+
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+ }
+
+ /* Write ATDTW bit to 0 */
+ usb_slave_regs->usbcmd &= cpu_to_le32(~USB_CMD_ATDTW);
+
+ if (tmp_stat)
+ goto out;
+
+ /* fall through to Case 1: List is empty */
+ }
+
+ /* Write dQH next pointer and terminate bit to 0 */
+ WARN_ON(req->head->td_dma & 31);
+ dQH->next_dtd_ptr = cpu_to_le32(req->head->td_dma);
+
+ /* Clear active and halt bit */
+ temp = cpu_to_le32(~(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)));
+
+ pr_debug("udc: setting endpointprime. temp=0x%x (bitmask=0x%x)\n",
+ temp, bitmask);
+ usb_slave_regs->endpointprime |= cpu_to_le32(temp);
+
+ out:
+ return 0;
+}
+
+static int arcotg_build_dtd(struct arcotg_req *req, unsigned max,
+ struct ep_td_struct **address,
+ struct arcotg_udc *udc)
+{
+ unsigned length;
+ u32 swap_temp;
+ struct ep_td_struct *dtd;
+ dma_addr_t handle;
+
+ /* how big will this packet be? */
+ length = min(req->req.length - req->req.actual, max);
+
+ dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, &handle);
+ pr_debug("udc: dma_pool_alloc() ep(0x%p)=%s virt=0x%p dma=0x%x\n",
+ req->ep, req->ep->name, dtd, handle);
+
+ /* check alignment - must be 32 byte aligned (bits 4:0 == 0) */
+ BUG_ON((u32) dtd & 31);
+
+ memset(dtd, 0, sizeof(struct ep_td_struct));
+ dtd->td_dma = handle;
+
+ /* Fill in the transfer size; set interrupt on every dtd;
+ set active bit */
+ swap_temp = ((length << DTD_LENGTH_BIT_POS) | DTD_IOC
+ | DTD_STATUS_ACTIVE);
+
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ /* Clear reserved field */
+ swap_temp = cpu_to_le32(dtd->size_ioc_sts);
+ swap_temp &= ~DTD_RESERVED_FIELDS;
+ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
+
+ pr_debug("udc: req=0x%p dtd=0x%p req.dma=0x%x req.length=%d "
+ "length=%d size_ioc_sts=0x%x\n",
+ req, dtd, req->req.dma, req->req.length,
+ length, dtd->size_ioc_sts);
+
+ /* Init all of buffer page pointers */
+ swap_temp = (u32) (req->req.dma + req->req.actual);
+ dtd->buff_ptr0 = cpu_to_le32(swap_temp);
+ dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
+ dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
+ dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
+ dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
+
+ req->req.actual += length;
+ *address = dtd;
+
+ return length;
+}
+
+/*!
+ * add USB request to dtd queue
+ * @param req USB request
+ * @param dev device pointer
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_req_to_dtd(struct arcotg_req *req, struct arcotg_udc *udc)
+{
+ unsigned max;
+ unsigned count;
+ int is_last;
+ int is_first = 1;
+ struct ep_td_struct *last_addr = NULL, *addr;
+
+ pr_debug("udc: req=0x%p\n", req);
+
+ max = EP_MAX_LENGTH_TRANSFER;
+ do {
+ count = arcotg_build_dtd(req, max, &addr, udc);
+
+ if (is_first) {
+ is_first = 0;
+ req->head = addr;
+ } else {
+ if (!last_addr) {
+ /* FIXME last_addr not set. iso only
+ * case, which we don't do yet
+ */
+ pr_debug("udc: wiping out something at 0!!\n");
+ }
+
+ last_addr->next_td_ptr = cpu_to_le32(addr->td_dma);
+ last_addr->next_td_virt = addr;
+ last_addr = addr;
+ }
+
+ /* last packet is usually short (or a zlp) */
+ if (unlikely(count != max))
+ is_last = 1;
+ else if (likely(req->req.length != req->req.actual) ||
+ req->req.zero)
+ is_last = 0;
+ else
+ is_last = 1;
+
+ req->dtd_count++;
+ } while (!is_last);
+
+ addr->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
+ addr->next_td_virt = NULL;
+ req->tail = addr;
+
+ return 0;
+}
+
+/*!
+ * add transfer request to queue
+ * @param _ep endpoint pointer
+ * @param _req request pointer
+ * @param gfp_flags GFP_* flags to use
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ gfp_t gfp_flags)
+{
+ struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep);
+ struct arcotg_req *req = container_of(_req, struct arcotg_req, req);
+ struct arcotg_udc *udc;
+ unsigned long flags;
+ int is_iso = 0;
+
+ pr_debug("udc: _req=0x%p len=%d\n", _req, _req->length);
+
+ /* catch various bogus parameters */
+ if (!_req || !req->req.complete || !req->req.buf
+ || !list_empty(&req->queue)) {
+ pr_debug("udc: %s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (!_ep || (!ep->desc && ep_index(ep))) {
+ pr_debug("udc: %s, bad ep\n", __FUNCTION__);
+ 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;
+
+ /* if data phase is absent send the status phase */
+ if ((ep_index(ep) == 0)) {
+ if (udc->ep0_state != DATA_STATE_XMIT &&
+ udc->ep0_state != DATA_STATE_RECV &&
+ (udc->local_setup_buff).wLength == 0) {
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ Ep0Stall(udc);
+ else
+ return 0;
+ }
+ }
+
+ /* 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;
+ pr_debug("udc: called dma_map_single(buffer,%s) req=0x%p "
+ "buf=0x%p dma=0x%x len=%d\n",
+ ep_is_in(ep) ? "to_dvc" : "from_dvc",
+ req, req->req.buf, req->req.dma, req->req.length);
+ pr_debug("udc: req=0x%p set req.dma=0x%x\n", req, req->req.dma);
+ } 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;
+ pr_debug("udc: called dma_sync_single_for_device(buffer,%s) "
+ "req=0x%p buf=0x%p dma=0x%x len=%d\n",
+ ep_is_in(ep) ? "to_dvc" : "from_dvc",
+ req, req->req.buf, req->req.dma, req->req.length);
+ }
+
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->dtd_count = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* push the dtds to device queue */
+ if (!arcotg_req_to_dtd(req, udc))
+ arcotg_queue_td(ep, req);
+ else
+ return -ENOMEM;
+
+ /* EP0 */
+ if ((ep_index(ep) == 0)) {
+ udc->ep0_state = DATA_STATE_XMIT;
+ pr_debug("udc: ep0_state now DATA_STATE_XMIT\n");
+ }
+
+ /* put this req at the end of the ep's queue */
+ /* irq handler advances the queue */
+ if (req != NULL)
+ list_add_tail(&req->queue, &ep->queue);
+
+ dump_ep_queue(ep);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/*!
+ * remove the endpoint buffer
+ * @param _ep endpoint pointer
+ * @param _req usb request pointer
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct arcotg_ep *ep = container_of(_ep, struct arcotg_ep, ep);
+ struct arcotg_req *req;
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->udc->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->udc->lock, flags);
+ return -EINVAL;
+ }
+ pr_debug("udc: req=0x%p\n", req);
+
+ done(ep, req, -ECONNRESET);
+
+ spin_unlock_irqrestore(&ep->udc->lock, flags);
+ return 0;
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*!
+ * modify the endpoint halt feature
+ * @param _ep the non-isochronous endpoint being stalled
+ * @param value 1--set halt 0--clear halt
+ * @return Returns zero, or a negative error code
+ */
+static int _arcotg_ep_set_halt(struct usb_ep *_ep, int value)
+{
+
+ struct arcotg_ep *ep = NULL;
+ unsigned long flags = 0;
+ int status = -EOPNOTSUPP; /* operation not supported */
+ unsigned char ep_dir = 0, ep_num = 0;
+ struct arcotg_udc *udc = NULL;
+
+ ep = container_of(_ep, struct arcotg_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;
+ }
+
+ /* Attemp 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_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+ }
+ out:
+ pr_debug("udc: %s %s halt rc=%d\n",
+ ep->ep.name, value ? "set" : "clear", status);
+
+ return status;
+}
+
+static int arcotg_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ return (_arcotg_ep_set_halt(_ep, value));
+}
+
+static int arcotg_fifo_status(struct usb_ep *_ep)
+{
+ struct arcotg_ep *ep;
+ struct arcotg_udc *udc;
+ int size = 0;
+ u32 bitmask;
+ struct ep_queue_head *d_qh;
+
+ ep = container_of(_ep, struct arcotg_ep, ep);
+ if (!_ep || (!ep->desc && ep_index(ep) != 0))
+ return -ENODEV;
+
+ udc = (struct arcotg_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 (le32_to_cpu(usb_slave_regs->endptstatus) & bitmask)
+ size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE)
+ >> DTD_LENGTH_BIT_POS;
+
+ pr_debug("%s %u\n", __FUNCTION__, size);
+ return size;
+}
+
+static void arcotg_fifo_flush(struct usb_ep *_ep)
+{
+ struct arcotg_ep *ep;
+ struct arcotg_udc *udc;
+ u32 bitmask;
+ int loops = 0;
+
+ ep = container_of(_ep, struct arcotg_ep, ep);
+ if (!_ep || (!ep->desc && ep_index(ep) != 0))
+ return;
+
+ udc = (struct arcotg_udc *)ep->udc;
+
+ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
+ return;
+
+ bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) :
+ (1 << (ep_index(ep)));
+
+ do {
+ /* set the flush bit, and wait for it to clear */
+ usb_slave_regs->endptflush = cpu_to_le32(bitmask);
+ while (usb_slave_regs->endptflush)
+ continue;
+
+ /* if ENDPTSTAT bit is set, the flush failed. Retry. */
+ if (!(cpu_to_le32(usb_slave_regs->endptstatus) && bitmask))
+ break;
+ } while (++loops > 3);
+}
+
+/*!
+ * endpoint callback functions
+ */
+static const struct usb_ep_ops arcotg_ep_ops = {
+ .enable = arcotg_ep_enable,
+ .disable = arcotg_ep_disable,
+
+ .alloc_request = arcotg_alloc_request,
+ .free_request = arcotg_free_request,
+
+ .alloc_buffer = arcotg_alloc_buffer,
+ .free_buffer = arcotg_free_buffer,
+
+ .queue = arcotg_ep_queue,
+ .dequeue = arcotg_ep_dequeue,
+
+ .set_halt = arcotg_ep_set_halt,
+
+ .fifo_status = arcotg_fifo_status,
+ .fifo_flush = arcotg_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------
+ Gadget Driver Layer Operations
+-------------------------------------------------------------------------*/
+
+/*************************************************************************
+ Gadget Driver Layer Operations
+*************************************************************************/
+
+/*!
+ * Get the current frame number (from DR frame_index Reg )
+ * @param gadget gadget pointer
+ * @return return frame count
+ */
+static int arcotg_get_frame(struct usb_gadget *gadget)
+{
+ return (int)(le32_to_cpu(usb_slave_regs->frindex) & USB_FRINDEX_MASKS);
+}
+
+/*-----------------------------------------------------------------------
+ * Tries to wake up the host connected to this gadget
+ *
+ * Return : 0-success
+ * Negative-this feature not enabled by host or not supported by device hw
+ * FIXME: RM 16.6.2.2.1 DR support this wake-up feature?
+ -----------------------------------------------------------------------*/
+static int arcotg_wakeup(struct usb_gadget *gadget)
+{
+ pr_debug("%s\n", __FUNCTION__);
+ return -ENOTSUPP;
+}
+
+/*!
+ * sets the device selfpowered feature
+ * this affects the device status reported by the hw driver
+ * to reflect that it now has a local power supply
+ * usually device hw has register for this feature
+ * @param gadget gadget pointer
+ * @param is_selfpowered self powered?
+ * @return if selfpowered
+ */
+static int arcotg_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+{
+ pr_debug("%s\n", __FUNCTION__);
+ return -ENOTSUPP;
+}
+
+static int can_pullup(struct arcotg_udc *udc)
+{
+ return udc->driver && udc->softconnect && udc->vbus_active;
+}
+
+/*!
+ * Notify controller that VBUS is powered, Called by whatever
+ * detects VBUS sessions
+ * @param gadger gadger pointer
+ * @param is_active is active?
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+ struct arcotg_udc *udc;
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ udc = container_of(gadget, struct arcotg_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+
+ pr_debug("udc: VBUS %s\n", is_active ? "on" : "off");
+ udc->vbus_active = (is_active != 0);
+
+#if 0 /* FIXME manipulate pullups?? check other platforms. */
+ if (can_pullup(udc))
+ usb_slave_regs->usbcmd |= USB_CMD_RUN_STOP;
+ else
+ usb_slave_regs->usbcmd &= ~USB_CMD_RUN_STOP;
+#endif
+
+ 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.
+ * @param gadget gadger pointer
+ * @param mA power
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+ struct arcotg_udc *udc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ udc = container_of(gadget, struct arcotg_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
+ * @param gadget gadger pointer
+ * @param is_on on or off
+ * @return Returns zero on success , or a negative error code
+ */
+static int arcotg_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct arcotg_udc *udc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ udc = container_of(gadget, struct arcotg_udc, gadget);
+ udc->softconnect = (is_on != 0);
+ if (can_pullup(udc))
+ usb_slave_regs->usbcmd |= USB_CMD_RUN_STOP;
+ else
+ usb_slave_regs->usbcmd &= ~USB_CMD_RUN_STOP;
+
+ return 0;
+}
+
+static const struct usb_gadget_ops arcotg_gadget_ops = {
+ .get_frame = arcotg_get_frame,
+ .wakeup = arcotg_wakeup,
+ .set_selfpowered = arcotg_set_selfpowered,
+ .vbus_session = arcotg_vbus_session,
+ .vbus_draw = arcotg_vbus_draw,
+ .pullup = arcotg_pullup,
+};
+
+static void Ep0Stall(struct arcotg_udc *udc)
+{
+ u32 tmp;
+
+ pr_debug("%s\n", __FUNCTION__);
+ /* a protocol stall */
+ tmp = le32_to_cpu(usb_slave_regs->endptctrl[0]);
+ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
+ usb_slave_regs->endptctrl[0] = cpu_to_le32(tmp);
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+}
+
+/*!
+ * if direction is EP_IN, the status is Device->Host
+ * if direction is EP_OUT, the status transaction is Device<-Host
+ * @param udc device controller pointer
+ * @param direction in or out
+ * @return Returns zero on success , or a negative error code
+ */
+static int ep0_prime_status(struct arcotg_udc *udc, int direction)
+{
+
+ struct arcotg_req *req = udc->status_req;
+ struct arcotg_ep *ep;
+ int status = 0;
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+ if (direction == EP_DIR_IN)
+ udc->ep0_dir = USB_DIR_IN;
+ else
+ udc->ep0_dir = USB_DIR_OUT;
+
+ ep = &udc->eps[0];
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+ pr_debug("udc: ep0_state now WAIT_FOR_OUT_STATUS\n");
+
+ req->ep = ep;
+ req->req.length = 0;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+ req->req.complete = NULL;
+ req->dtd_count = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ if ((arcotg_req_to_dtd(req, udc) == 0))
+ status = arcotg_queue_td(ep, req);
+ else
+ return -ENOMEM;
+
+ if (status)
+ printk(KERN_ERR "Can't get control status request \n");
+
+ list_add_tail(&req->queue, &ep->queue);
+ dump_ep_queue(ep);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return status;
+}
+
+static int udc_reset_ep_queue(struct arcotg_udc *udc, u8 pipe)
+{
+ struct arcotg_ep *ep = get_ep_by_pipe(udc, pipe);
+
+ pr_debug("%s\n", __FUNCTION__);
+ /* FIXME: collect completed requests? */
+ if (!ep->name)
+ return 0;
+
+ nuke(ep, -ECONNRESET);
+
+ return 0;
+}
+static void ch9SetAddress(struct arcotg_udc *udc, u16 value, u16 index,
+ u16 length)
+{
+ pr_debug("udc: new address=%d\n", value);
+
+ /* 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);
+}
+
+static void ch9GetStatus(struct arcotg_udc *udc, u16 value, u16 index,
+ u16 length)
+{
+ u16 usb_status = 0; /* fix me to give correct status */
+
+ struct arcotg_req *req;
+ struct arcotg_ep *ep;
+ int status = 0;
+ unsigned long flags;
+
+ pr_debug("%s\n", __FUNCTION__);
+ ep = &udc->eps[0];
+
+ req = container_of(arcotg_alloc_request(&ep->ep, GFP_KERNEL),
+ struct arcotg_req, req);
+ req->req.length = 2;
+ req->req.buf = &usb_status;
+ req->req.status = -EINPROGRESS;
+ req->req.actual = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ /* data phase */
+ if ((arcotg_req_to_dtd(req, udc) == 0))
+ status = arcotg_queue_td(ep, req);
+ else /* no mem */
+ goto stall;
+
+ if (status) {
+ printk(KERN_ERR "Can't respond to getstatus request \n");
+ Ep0Stall(udc);
+ } else {
+ udc->ep0_state = DATA_STATE_XMIT;
+ pr_debug("udc: ep0_state now DATA_STATE_XMIT\n");
+ }
+
+ list_add_tail(&req->queue, &ep->queue);
+ dump_ep_queue(ep);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return;
+
+ stall:
+ Ep0Stall(udc);
+}
+
+static void ch9SetConfig(struct arcotg_udc *udc, u16 value, u16 index,
+ u16 length)
+{
+ pr_debug("udc: 1 calling gadget driver->setup\n");
+ udc->ep0_dir = USB_DIR_IN;
+ if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff) >= 0) {
+ /* gadget layer deal with the status phase */
+ udc->usb_state = USB_STATE_CONFIGURED;
+ udc->ep0_state = WAIT_FOR_OUT_STATUS;
+ pr_debug("udc: ep0_state now WAIT_FOR_OUT_STATUS\n");
+ }
+}
+
+static void setup_received_irq(struct arcotg_udc *udc,
+ struct usb_ctrlrequest *setup)
+{
+ u16 ptc = 0; /* port test control */
+ int handled = 1; /* set to zero if we do not handle the message, */
+ /* and should pass it to the gadget driver */
+
+ pr_debug("udc: request=0x%x\n", setup->bRequest);
+ /* Fix Endian (udc->local_setup_buff is cpu Endian now) */
+ setup->wValue = le16_to_cpu(setup->wValue);
+ setup->wIndex = le16_to_cpu(setup->wIndex);
+ setup->wLength = le16_to_cpu(setup->wLength);
+
+ udc_reset_ep_queue(udc, 0);
+
+ /* We asume setup only occurs on EP0 */
+ if (setup->bRequestType & USB_DIR_IN)
+ udc->ep0_dir = USB_DIR_IN;
+ else
+ udc->ep0_dir = USB_DIR_OUT;
+
+ if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+ /* handle class requests */
+ switch (setup->bRequest) {
+
+ case USB_BULK_RESET_REQUEST:
+ udc->ep0_dir = USB_DIR_IN;
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) >= 0) {
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ }
+ break;
+
+ default:
+ handled = 0;
+ break;
+ }
+ } else if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ /* handle standard requests */
+ switch (setup->bRequest) {
+
+ case USB_REQ_GET_STATUS:
+ if ((setup->
+ bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD))
+ != (USB_DIR_IN | USB_TYPE_STANDARD))
+ break;
+ ch9GetStatus(udc, setup->wValue, setup->wIndex,
+ setup->wLength);
+ break;
+
+ case USB_REQ_SET_ADDRESS:
+ if (setup->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_STANDARD |
+ USB_RECIP_DEVICE))
+ break;
+ ch9SetAddress(udc, setup->wValue, setup->wIndex,
+ setup->wLength);
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ if (setup->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_STANDARD |
+ USB_RECIP_DEVICE))
+ break;
+ /* gadget layer take over the status phase */
+ ch9SetConfig(udc, setup->wValue, setup->wIndex,
+ setup->wLength);
+ break;
+ case USB_REQ_SET_INTERFACE:
+ if (setup->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_STANDARD |
+ USB_RECIP_INTERFACE))
+ break;
+ udc->ep0_dir = USB_DIR_IN;
+ if (udc->driver->setup(&udc->gadget,
+ &udc->local_setup_buff) >= 0)
+ /* gadget layer take over the status phase */
+ break;
+ /* Requests with no data phase */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ { /* status transaction */
+ int rc = -EOPNOTSUPP;
+
+ if ((setup->bRequestType & USB_TYPE_MASK) !=
+ USB_TYPE_STANDARD)
+ break;
+
+ /* we only support set/clear feature for endpoint */
+ if (setup->bRequestType == USB_RECIP_ENDPOINT) {
+ int dir = (setup->wIndex & 0x0080) ?
+ EP_DIR_IN : EP_DIR_OUT;
+ int num = (setup->wIndex & 0x000f);
+ struct arcotg_ep *ep;
+
+ if (setup->wValue != 0
+ || setup->wLength != 0
+ || (num * 2 + dir) > USB_MAX_PIPES)
+ break;
+ ep = &udc->eps[num * 2 + dir];
+
+ if (setup->bRequest ==
+ USB_REQ_SET_FEATURE) {
+ pr_debug("udc: udc: SET_FEATURE"
+ " doing set_halt\n");
+ rc = _arcotg_ep_set_halt(&ep->
+ ep, 1);
+ } else {
+ pr_debug("udc: CLEAR_FEATURE"
+ " doing clear_halt\n");
+ rc = _arcotg_ep_set_halt(&ep->
+ ep, 0);
+ }
+
+ } else if (setup->bRequestType ==
+ USB_RECIP_DEVICE) {
+ if (setup->bRequest ==
+ USB_REQ_SET_FEATURE) {
+ ptc = setup->wIndex >> 8;
+ rc = 0;
+ }
+ if (!udc->gadget.is_otg)
+ break;
+ else 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;
+ }
+ if (rc == 0) {
+ /* send status only if _arcotg_ep_set_halt success */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ Ep0Stall(udc);
+ }
+ break;
+ }
+ default:
+ handled = 0;
+ break;
+ }
+ } else {
+ /* vendor requests */
+ handled = 0;
+ }
+
+ if (!handled) {
+ if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff)
+ != 0) {
+ Ep0Stall(udc);
+ } else if (setup->bRequestType & USB_DIR_IN) {
+ udc->ep0_state = DATA_STATE_XMIT;
+ pr_debug("udc: ep0_state now DATA_STATE_XMIT\n");
+ } else {
+ if (setup->wLength != 0) {
+ udc->ep0_state = DATA_STATE_RECV;
+ pr_debug
+ ("udc: ep0_state now DATA_STATE_RECV\n");
+ }
+ }
+ }
+
+ if (ptc) {
+ usb_slave_regs->portsc1 |= ptc << 16;
+ pr_debug("udc: switch to test mode.\n");
+ }
+}
+
+static void ep0_req_complete(struct arcotg_udc *udc, struct arcotg_ep *ep0,
+ struct arcotg_req *req)
+{
+ pr_debug("udc: req=0x%p ep0_state=0x%x\n", req, udc->ep0_state);
+ if (udc->usb_state == USB_STATE_ADDRESS) {
+ /* Set the new address */
+ u32 new_address = (u32) udc->device_address;
+ usb_slave_regs->deviceaddr = cpu_to_le32(new_address <<
+ USB_DEVICE_ADDRESS_BIT_POS);
+ pr_debug("udc: set deviceaddr to %d\n",
+ usb_slave_regs->
+ deviceaddr >> USB_DEVICE_ADDRESS_BIT_POS);
+ }
+
+ switch (udc->ep0_state) {
+ case DATA_STATE_XMIT:
+
+ done(ep0, req, 0);
+ /* receive status phase */
+ if (ep0_prime_status(udc, EP_DIR_OUT))
+ Ep0Stall(udc);
+ break;
+
+ case DATA_STATE_RECV:
+
+ done(ep0, req, 0);
+ /* send status phase */
+ if (ep0_prime_status(udc, EP_DIR_IN))
+ Ep0Stall(udc);
+ break;
+
+ case WAIT_FOR_OUT_STATUS:
+ done(ep0, req, 0);
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ break;
+
+ case WAIT_FOR_SETUP:
+ pr_debug("udc: Unexpected interrupt\n");
+ break;
+
+ default:
+ Ep0Stall(udc);
+ break;
+ }
+}
+
+static void tripwire_handler(struct arcotg_udc *udc, u8 ep_num, u8 * buffer_ptr)
+{
+ u32 temp;
+ struct ep_queue_head *qh;
+
+ qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
+
+ /* Clear bit in ENDPTSETUPSTAT */
+ temp = cpu_to_le32(1 << ep_num);
+ usb_slave_regs->endptsetupstat |= temp;
+
+ /* while a hazard exists when setup package arrives */
+ do {
+ /* Set Setup Tripwire */
+ temp = cpu_to_le32(USB_CMD_SUTW);
+ usb_slave_regs->usbcmd |= temp;
+
+ /* Copy the setup packet to local buffer */
+ pr_debug("udc: qh=0x%p copy setup buffer from 0x%p to 0x%p\n",
+ qh, qh->setup_buffer, buffer_ptr);
+ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
+ } while (!(le32_to_cpu(usb_slave_regs->usbcmd) & USB_CMD_SUTW));
+
+ /* Clear Setup Tripwire */
+ temp = le32_to_cpu(usb_slave_regs->usbcmd);
+ temp &= ~USB_CMD_SUTW;
+ usb_slave_regs->usbcmd = le32_to_cpu(temp);
+
+ timeout = 10000000;
+ while ((usb_slave_regs->endptsetupstat & 1) && --timeout) {
+ continue;
+ }
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+ }
+}
+
+/*!
+ * process_ep_req(): free the completed Tds for this req
+ * FIXME: ERROR handling for multi-dtd requests
+ * @param udc device controller pointer
+ * @param pipe endpoint pipe
+ * @param req request pointer
+ * @return Returns zero on success , or a negative error code
+ */
+static int process_ep_req(struct arcotg_udc *udc, int pipe,
+ struct arcotg_req *curr_req)
+{
+ struct ep_td_struct *curr_td, *tmp_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;
+
+ curr_td = curr_req->head;
+ td_complete = 0;
+ actual = curr_req->req.length;
+
+ pr_debug
+ ("udc: curr_req=0x%p curr_td=0x%p actual=%d size_ioc_sts=0x%x\n",
+ curr_req, curr_td, actual, curr_td->size_ioc_sts);
+
+ for (j = 0; j < curr_req->dtd_count; j++) {
+ remaining_length = ((le32_to_cpu(curr_td->size_ioc_sts)
+ & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS);
+ actual -= remaining_length;
+
+ if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
+ DTD_ERROR_MASK)) {
+ if (errors & DTD_STATUS_HALTED) {
+ printk(KERN_ERR "dTD error %08x \n", errors);
+ /* Clear the errors and Halt condition */
+ tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
+ tmp &= ~errors;
+ curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
+ status = -EPIPE;
+ /*FIXME clearing active bit, update
+ * nextTD pointer re-prime ep */
+
+ break;
+ }
+ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+ pr_debug("udc: Transfer overflow\n");
+ status = -EPROTO;
+ break;
+ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+ pr_debug("udc: ISO error\n");
+ status = -EILSEQ;
+ break;
+ } else
+ printk(KERN_ERR
+ "Unknown error has occured (0x%x)!\r\n",
+ errors);
+
+ } else if (le32_to_cpu(curr_td->size_ioc_sts) &
+ DTD_STATUS_ACTIVE) {
+ pr_debug("udc: Request not wholly complete dtd=0x%p\n",
+ curr_td);
+ status = REQ_UNCOMPLETE;
+ return status;
+ } else if (remaining_length)
+ if (direction) {
+ pr_debug
+ ("udc: Transmit dTD remaining length not zero "
+ "(rl=%d)\n", remaining_length);
+ status = -EPROTO;
+ break;
+ } else {
+ td_complete += 1;
+ break;
+ } else {
+ td_complete += 1;
+ pr_debug("udc: dTD transmitted successful\n");
+ }
+
+ if (j != curr_req->dtd_count - 1)
+ curr_td = curr_td->next_td_virt;
+ }
+
+ if (status)
+ return status;
+
+ curr_req->req.actual = actual;
+
+ /* Free dtd for completed/error request */
+ curr_td = curr_req->head;
+ for (j = 0; j < curr_req->dtd_count; j++) {
+ tmp_td = curr_td;
+ if (j != curr_req->dtd_count - 1)
+ curr_td = curr_td->next_td_virt;
+ pr_debug("udc: freeing dtd 0x%p curr_req=0x%p\n", tmp_td,
+ curr_req);
+ dma_pool_free(udc->dtd_pool, tmp_td, tmp_td->td_dma);
+ }
+
+ return status;
+}
+
+static void dtd_complete_irq(struct arcotg_udc *udc)
+{
+ u32 bit_pos;
+ int i, ep_num, direction, bit_mask, status;
+ struct arcotg_ep *curr_ep;
+ struct arcotg_req *curr_req, *temp_req;
+
+ pr_debug("%s\n", __FUNCTION__);
+ /* Clear the bits in the register */
+ bit_pos = usb_slave_regs->endptcomplete;
+ usb_slave_regs->endptcomplete = bit_pos;
+ bit_pos = le32_to_cpu(bit_pos);
+
+ if (!bit_pos)
+ return;
+
+ for (i = 0; i < USB_MAX_ENDPOINTS * 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) {
+ printk(KERN_WARNING "Invalid EP?\n");
+ continue;
+ }
+
+ dump_ep_queue(curr_ep);
+
+ /* search all arcotg_reqs of ep */
+ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
+ queue) {
+ status = process_ep_req(udc, i, curr_req);
+ if (status == REQ_UNCOMPLETE) {
+ pr_debug
+ ("udc: Not all tds are completed in the req\n");
+ break;
+ }
+
+ if (ep_num == 0) {
+ ep0_req_complete(udc, curr_ep, curr_req);
+ break;
+ } else
+ done(curr_ep, curr_req, status);
+
+ }
+
+ dump_ep_queue(curr_ep);
+ }
+}
+
+static void port_change_irq(struct arcotg_udc *udc)
+{
+ u32 speed;
+
+ if (udc->bus_reset)
+ udc->bus_reset = FALSE;
+
+ /* Bus resetting is finished */
+ if (!(le32_to_cpu(usb_slave_regs->portsc1) & PORTSCX_PORT_RESET)) {
+ /* Get the speed */
+ speed = (le32_to_cpu(usb_slave_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;
+ }
+ }
+ pr_debug("udc: speed now %d\n", udc->gadget.speed);
+
+ /* Update USB state */
+ if (!udc->resume_state)
+ udc->usb_state = USB_STATE_DEFAULT;
+}
+
+static void suspend_irq(struct arcotg_udc *udc)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ udc->resume_state = udc->usb_state;
+ udc->usb_state = USB_STATE_SUSPENDED;
+
+ /* report suspend to the driver ,serial.c not support this */
+ if (udc->driver->suspend)
+ udc->driver->suspend(&udc->gadget);
+}
+
+static void resume_irq(struct arcotg_udc *udc)
+{
+ pr_debug("%s\n", __FUNCTION__);
+
+ udc->usb_state = udc->resume_state;
+ udc->resume_state = 0;
+
+ /* report resume to the driver , serial.c not support this */
+ if (udc->driver->resume)
+ udc->driver->resume(&udc->gadget);
+
+}
+
+static int reset_queues(struct arcotg_udc *udc)
+{
+ u8 pipe;
+
+ pr_debug("udc: disconnect\n");
+ 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;
+}
+
+static void reset_irq(struct arcotg_udc *udc)
+{
+ u32 temp;
+
+ /* Clear the device address */
+ temp = le32_to_cpu(usb_slave_regs->deviceaddr);
+ temp &= ~USB_DEVICE_ADDRESS_MASK;
+ usb_slave_regs->deviceaddr = cpu_to_le32(temp);
+ pr_debug("udc: set deviceaddr to %d\n",
+ usb_slave_regs->deviceaddr >> USB_DEVICE_ADDRESS_BIT_POS);
+ udc->device_address = 0;
+
+ /* Clear usb state */
+ udc->usb_state = USB_STATE_DEFAULT;
+
+ /* Clear all the setup token semaphores */
+ temp = le32_to_cpu(usb_slave_regs->endptsetupstat);
+ usb_slave_regs->endptsetupstat = cpu_to_le32(temp);
+
+ /* Clear all the endpoint complete status bits */
+ temp = le32_to_cpu(usb_slave_regs->endptcomplete);
+ usb_slave_regs->endptcomplete = cpu_to_le32(temp);
+
+ timeout = 10000000;
+ /* Wait until all endptprime bits cleared */
+ while ((usb_slave_regs->endpointprime) && --timeout) {
+ continue;
+ }
+ if (timeout == 0) {
+ printk(KERN_ERR "%s: TIMEOUT\n", __FUNCTION__);
+ }
+
+ /* Write 1s to the Flush register */
+ usb_slave_regs->endptflush = 0xFFFFFFFF;
+
+ if (le32_to_cpu(usb_slave_regs->portsc1) & PORTSCX_PORT_RESET) {
+ pr_debug("udc: Bus RESET\n");
+ /* Bus is reseting */
+ udc->bus_reset = TRUE;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+ /* Reset all the queues, include XD, dTD, EP queue
+ * head and TR Queue */
+ reset_queues(udc);
+ } else {
+ pr_debug("udc: Controller reset\n");
+ /* initialize usb hw reg except for regs for EP, not
+ * touch usbintr reg */
+ dr_controller_setup(udc);
+
+ /* FIXME: Reset all internal used Queues */
+ reset_queues(udc);
+
+ ep0_dr_and_qh_setup(udc);
+
+ /* Enable DR IRQ reg, Set Run bit, change udc state */
+ dr_controller_run(udc);
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+ }
+}
+
+static irqreturn_t arcotg_udc_irq(int irq, void *_udc)
+{
+ struct arcotg_udc *udc = _udc;
+ u32 irq_src;
+ irqreturn_t status = IRQ_NONE;
+ unsigned long flags;
+
+ if (udc->stopped)
+ return IRQ_NONE; /* ignore irq if we're not running */
+
+ spin_lock_irqsave(&udc->lock, flags);
+ irq_src = usb_slave_regs->usbsts & usb_slave_regs->usbintr;
+ /* Clear notification bits */
+ usb_slave_regs->usbsts &= irq_src;
+
+ irq_src = le32_to_cpu(irq_src);
+ pr_debug("udc: irq_src [0x%08x]\n", irq_src);
+
+ /* USB Interrupt */
+ if (irq_src & USB_STS_INT) {
+ /* Setup packet, we only support ep0 as control ep */
+ pr_debug("udc: endptsetupstat=0x%x endptcomplete=0x%x\n",
+ usb_slave_regs->endptsetupstat,
+ usb_slave_regs->endptcomplete);
+ if (usb_slave_regs->
+ endptsetupstat & cpu_to_le32(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 (usb_slave_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) {
+ reset_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ /* Sleep Enable (Suspend) */
+ if (irq_src & USB_STS_SUSPEND) {
+ suspend_irq(udc);
+ status = IRQ_HANDLED;
+ } else if (udc->resume_state) {
+ resume_irq(udc);
+ status = IRQ_HANDLED;
+ }
+
+ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+ pr_debug("udc: Error IRQ %x\n", irq_src);
+ status = IRQ_HANDLED;
+ }
+
+ if (status != IRQ_HANDLED) {
+ pr_debug("udc: not handled irq_src=0x%x\n", irq_src);
+ }
+
+ pr_debug("udc: irq_src [0x%08x] done. regs now=0x%08x\n", irq_src,
+ usb_slave_regs->usbsts & usb_slave_regs->usbintr);
+ pr_debug("-\n");
+ pr_debug("-\n");
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return status;
+}
+
+/*!
+ * tell the controller driver about gadget layer driver
+ * The driver's bind function will be called to bind it to a gadget.
+ * @param driver for example fsg_driver from file_storage.c
+ * @return Returns zero on success , or a negative error code
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ int retval = -ENODEV;
+ unsigned long flags = 0;
+ struct arcotg_udc *udc = udc_controller;
+
+ pr_debug("udc: udc=0x%p\n", udc);
+
+ /* standard operations */
+ if (!udc)
+ return -ENODEV;
+
+ if (!driver || (driver->speed != USB_SPEED_FULL
+ && driver->speed != USB_SPEED_HIGH)
+ || !driver->bind || !driver->unbind ||
+ !driver->disconnect || !driver->setup)
+ return -EINVAL;
+
+ if (udc->driver)
+ return -EBUSY;
+
+ /* lock is needed but whether should use this lock or another */
+ spin_lock_irqsave(&udc->lock, flags);
+
+ driver->driver.bus = 0;
+ /* hook up the driver */
+ udc->driver = driver;
+ udc->gadget.dev.driver = &driver->driver;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ retval = driver->bind(&udc->gadget);
+ if (retval) {
+ pr_debug("bind to %s --> %d\n", driver->driver.name, retval);
+ udc->gadget.dev.driver = 0;
+ udc->driver = 0;
+ goto out;
+ }
+
+ if (udc->transceiver) {
+ /* Suspend the controller until OTG enables it */
+ udc_suspend(udc);
+ pr_debug("udc: suspend udc for OTG auto detect \n");
+
+ /* export udc suspend/resume call to OTG */
+ udc->gadget.dev.parent->driver->suspend = arcotg_udc_suspend;
+ udc->gadget.dev.parent->driver->resume = arcotg_udc_resume;
+
+ /* connect to bus through transceiver */
+ retval = otg_set_peripheral(udc->transceiver, &udc->gadget);
+ if (retval < 0) {
+ pr_debug("udc: can't bind to transceiver\n");
+ driver->unbind(&udc->gadget);
+ udc->gadget.dev.driver = 0;
+ udc->driver = 0;
+ return retval;
+ }
+ } else {
+ /* Enable DR IRQ reg and Set usbcmd reg Run bit */
+ dr_controller_run(udc);
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+ }
+
+ printk(KERN_INFO "arcotg_udc: gadget %s bound to driver %s\n",
+ udc->gadget.name, driver->driver.name);
+
+ out:
+ return retval;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct arcotg_ep *loop_ep;
+ unsigned long flags;
+ struct arcotg_udc *udc = udc_controller;
+
+ pr_debug("usb_gadget_unregister_driver: udc=0x%p\n", udc);
+ if (!udc)
+ return -ENODEV;
+
+ if (!driver || driver != udc->driver)
+ return -EINVAL;
+
+ if (udc->transceiver) {
+ (void)otg_set_peripheral(udc->transceiver, 0);
+ pr_debug("udc: set peripheral=NULL\n");
+ } else {
+ /* FIXME
+ pullup_disable(udc);
+ */
+ }
+
+ /* stop DR, disable intr */
+ dr_controller_stop(udc);
+
+ /* in fact, no needed */
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ pr_debug("udc: ep0_state now WAIT_FOR_SETUP\n");
+ udc->ep0_dir = 0;
+
+ /* stand operation */
+ spin_lock_irqsave(&udc->lock, flags);
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ nuke(&udc->eps[0], -ESHUTDOWN);
+ list_for_each_entry(loop_ep, &udc->gadget.ep_list, ep.ep_list)
+ nuke(loop_ep, -ESHUTDOWN);
+
+ /* report disconnect to free up endpoints */
+ pr_debug("udc: disconnect\n");
+ driver->disconnect(&udc->gadget);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ /* unbind gadget and unhook driver. */
+ pr_debug("udc: unbind\n");
+ driver->unbind(&udc->gadget);
+ udc->gadget.dev.driver = 0;
+ udc->driver = 0;
+
+ 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/arcotg_udc";
+
+static int arcotg_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 arcotg_ep *ep = NULL;
+ struct arcotg_req *req;
+
+ struct arcotg_udc *udc = udc_controller;
+ 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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 = le32_to_cpu(usb_slave_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 < USB_MAX_ENDPOINTS; i++) {
+ tmp_reg = le32_to_cpu(usb_slave_regs->endptctrl[i]);
+ t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
+ i, tmp_reg);
+ size -= t;
+ next += t;
+ }
+ tmp_reg = le32_to_cpu(usb_slave_regs->endpointprime);
+ t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ /* ------arcotg_udc, arcotg_ep, arcotg_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;
+ }
+ }
+ }
+#if 0 /* DDD debug */
+ else {
+ t = scnprintf(next, size, "\nno desc for %s\n",
+ ep->ep.name);
+ size -= t;
+ next += t;
+ }
+#endif
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_file() create_proc_read_entry(proc_filename, \
+ 0, NULL, arcotg_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 the ARC OTG specific udc structure
+ * it is not stand gadget function
+ * it is called when the last reference to the device is removed;
+ * it is called from the embedded kobject's release method.
+ * All device structures registered with the core must have a
+ * release method, or the kernel prints out scary complaints
+ * @param dev device controller pointer
+ */
+static void arcotg_gadget_release(struct device *dev)
+{
+ struct device *udc_dev = dev->parent;
+
+ struct arcotg_udc *udc = (struct arcotg_udc *)dev_get_drvdata(udc_dev);
+
+ complete(udc->done);
+ dma_free_coherent(dev, udc->ep_qh_size, udc->ep_qh, udc->ep_qh_dma);
+ kfree(udc);
+}
+
+/******************************************************************
+ Internal Structure Build up functions -2
+*******************************************************************/
+/*!
+ * this func will init resource for globle controller
+ * Return the udc handle on success or Null on failing
+ * @param pdev device controller pointer
+ */
+static void *struct_udc_setup(struct platform_device *pdev)
+{
+ struct arcotg_udc *udc = NULL;
+
+ udc = (struct arcotg_udc *)
+ kmalloc(sizeof(struct arcotg_udc), GFP_KERNEL);
+ pr_debug("udc: kmalloc(ucd)=0x%p\n", udc);
+ if (udc == NULL) {
+ printk(KERN_ERR "malloc udc failed\n");
+ goto cleanup;
+ }
+
+ /* Zero out the internal USB state structure */
+ memset(udc, 0, sizeof(struct arcotg_udc));
+
+ /* initialized QHs, take care the 2K align */
+ udc->ep_qh_size = USB_MAX_PIPES * sizeof(struct ep_queue_head);
+
+ /* Arc OTG IP-core requires 2K alignment of queuehead
+ * this if fullfilled by per page allocation
+ * by dma_alloc_coherent(...)
+ */
+ udc->ep_qh = (struct ep_queue_head *)
+ dma_alloc_coherent(&pdev->dev, udc->ep_qh_size,
+ &udc->ep_qh_dma, GFP_KERNEL);
+ if (!udc->ep_qh) {
+ printk(KERN_ERR "malloc QHs for udc failed\n");
+ goto cleanup;
+ }
+ pr_debug("udc: udc->ep_qh=0x%p\n", udc->ep_qh);
+
+ memset(udc->ep_qh, 0, udc->ep_qh_size);
+
+ /* need 32 byte alignment, don't cross 4K boundary */
+ udc->dtd_pool = dma_pool_create("arcotg_dtd", &pdev->dev,
+ sizeof(struct ep_td_struct), 32, 4096);
+ if (!udc->dtd_pool) {
+ printk(KERN_ERR "dtd_pool alloc failed\n");
+ goto cleanup;
+ }
+
+ /* Initialize ep0 status request structure */
+ /* FIXME: arcotg_alloc_request() ignores ep argument */
+ udc->status_req =
+ container_of(arcotg_alloc_request(NULL, GFP_KERNEL),
+ struct arcotg_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);
+
+ pr_debug("udc: status_req=0x%p status_req->req.buf=0x%p "
+ "status_req->req.dma=0x%x",
+ udc->status_req, udc->status_req->req.buf,
+ udc->status_req->req.dma);
+
+ udc->resume_state = USB_STATE_NOTATTACHED;
+ udc->usb_state = USB_STATE_POWERED;
+ udc->ep0_dir = 0;
+ /* initliaze the arcotg_udc lock */
+ spin_lock_init(&udc->lock);
+
+ return udc;
+
+ cleanup:
+ kfree(udc);
+ return NULL;
+}
+
+/*!
+ * set up the arcotg_ep struct for eps
+ * ep0out isnot used so do nothing here
+ * ep0in should be taken care
+ * It also link this arcotg_ep->ep to gadget->ep_list
+ * @param udc device controller pointer
+ * @param pipe_num pipe number
+ * @return Returns zero on success , or a negative error code
+ */
+static int struct_ep_setup(struct arcotg_udc *udc, unsigned char pipe_num)
+{
+ struct arcotg_ep *ep = get_ep_by_pipe(udc, pipe_num);
+
+ /*
+ VDBG("pipe_num=%d name[%d]=%s",
+ pipe_num, pipe_num, ep_name[pipe_num]);
+ */
+ ep->udc = udc;
+ strcpy(ep->name, ep_name[pipe_num]);
+ ep->ep.name = ep_name[pipe_num];
+ ep->ep.ops = &arcotg_ep_ops;
+ ep->stopped = 0;
+
+ /* for ep0: the desc defined here;
+ * for other eps, gadget layer called ep_enable with defined desc
+ */
+ /* for ep0: maxP defined in desc
+ * for other eps, maxP is set by epautoconfig() called by gadget layer
+ */
+ if (pipe_num == 0) {
+ ep->desc = &arcotg_ep0_desc;
+ ep->ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+ } else {
+ ep->ep.maxpacket = (unsigned short)~0;
+ ep->desc = NULL;
+ }
+
+ /* the queue lists any req for this ep */
+ INIT_LIST_HEAD(&ep->queue);
+
+ /* arcotg_ep->ep.ep_list: gadget ep_list hold all of its eps
+ * so only the first should init--it is ep0' */
+
+ /* gagdet.ep_list used for ep_autoconfig so no ep0 */
+ if (pipe_num != 0)
+ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+
+ ep->gadget = &udc->gadget;
+
+ return 0;
+}
+
+static int board_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, pdata);
+
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->platform_init && pdata->platform_init(pdev) != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Driver probe functions */
+
+ /*!
+ * all intialize operations implemented here except Enable usb_intr reg
+ * @param dev device controller pointer
+ * @return Returns zero on success , or a negative error code
+ */
+static int __devinit arcotg_udc_probe(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct arcotg_udc *udc;
+
+ unsigned int tmp_status = -ENODEV;
+ unsigned int i;
+ u32 id;
+ u64 rsrc_start, rsrc_len;
+
+ if (strcmp(pdev->name, "arc_udc")) {
+ pr_debug("udc: Wrong device\n");
+ return -ENODEV;
+ }
+
+ pr_debug("%s: pdev=0x%p pdata=0x%p\n", __FUNCTION__, pdev, pdata);
+
+ if (board_init(pdev) != 0)
+ return -EINVAL;
+
+ /* Initialize the udc structure including QH member and other member */
+ udc = (struct arcotg_udc *)struct_udc_setup(pdev);
+ udc_controller = udc;
+
+ if (!udc) {
+ pr_debug("udc: udc is NULL\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&pdev->dev, udc);
+
+ udc->pdata = pdata;
+ udc->xcvr_type = pdata->xcvr_type;
+
+#ifdef CONFIG_USB_OTG
+ udc->transceiver = otg_get_transceiver();
+ pr_debug("udc: otg_get_transceiver returns 0x%p", udc->transceiver);
+#endif
+
+ if (pdev->resource[1].flags != IORESOURCE_IRQ) {
+ return -ENODEV;
+ }
+
+ rsrc_start = pdev->resource[0].start;
+ rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+ pr_debug("start=0x%x end=0x%x\n",
+ pdev->resource[0].start, pdev->resource[0].end);
+ pr_debug("rsrc_start=0x%llx rsrc_len=0x%llx\n", rsrc_start, rsrc_len);
+
+#if 0 /* DDD */
+ pr_debug("doing request_mem_region(start=0x%llx, len=0x%llx)\n",
+ rsrc_start, rsrc_len);
+ if (!request_mem_region(rsrc_start, rsrc_len, driver_name)) {
+ printk(KERN_ERR "request_mem_region failed\n");
+ return -EBUSY;
+ }
+#endif
+ usb_slave_regs = (struct usb_dr_device *)(int)IO_ADDRESS(rsrc_start);
+
+ pr_debug("udc: usb_slave_regs = 0x%p\n", usb_slave_regs);
+ pr_debug("udc: hci_version=0x%x\n", usb_slave_regs->hciversion);
+ pr_debug("udc: otgsc at 0x%p\n", &usb_slave_regs->otgsc);
+
+ id = usb_slave_regs->id;
+ printk(KERN_INFO "ARC USBOTG h/w ID=0x%x revision=0x%x\n",
+ id & 0x3f, id >> 16);
+
+ /* request irq and disable DR */
+ tmp_status = request_irq(pdev->resource[1].start, arcotg_udc_irq,
+ IRQF_SHARED, driver_name, udc);
+ if (tmp_status != 0) {
+ printk(KERN_ERR "cannot request irq %d err %d \n",
+ (int)pdev->resource[1].start, tmp_status);
+ /* DDD free mem_region here */
+ return tmp_status;
+ }
+
+ if (!udc->transceiver) {
+ /* initialize usb hw reg except for regs for EP,
+ * leave usbintr reg untouched*/
+ dr_controller_setup(udc);
+ }
+
+ /* here comes the stand operations for probe
+ * set the arcotg_udc->gadget.xxx
+ */
+ udc->gadget.ops = &arcotg_gadget_ops;
+ udc->gadget.is_dualspeed = 1;
+
+ /* gadget.ep0 is a pointer */
+ udc->gadget.ep0 = &udc->eps[0].ep;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* name: Identifies the controller hardware type. */
+ udc->gadget.name = driver_name;
+
+ device_initialize(&udc->gadget.dev);
+
+ strcpy(udc->gadget.dev.bus_id, "gadget");
+
+ udc->gadget.dev.release = arcotg_gadget_release;
+ udc->gadget.dev.parent = &pdev->dev;
+
+ if (udc->transceiver) {
+ udc->gadget.is_otg = 1;
+ }
+
+ /* for an EP, the intialization includes: fields in QH, Regs,
+ * arcotg_ep struct */
+ ep0_dr_and_qh_setup(udc);
+ for (i = 0; i < USB_MAX_PIPES; i++) {
+ /* because the ep type is not decide here so
+ * struct_ep_qh_setup() and dr_ep_setup()
+ * should be called in ep_enable()
+ */
+ if (ep_name[i] != NULL)
+ /* setup the arcotg_ep struct and link ep.ep.list
+ * into gadget.ep_list */
+ struct_ep_setup(udc, i);
+ }
+
+ create_proc_file();
+ tmp_status = device_add(&udc->gadget.dev);
+ pr_debug("udc: back from device_add ");
+
+ return tmp_status;
+}
+
+/*!
+ * Driver removal functions
+ * Free resources
+ * Finish pending transaction
+ * @param dev device controller pointer
+ * @return Returns zero on success , or a negative error code
+ */
+static int __devexit arcotg_udc_remove(struct platform_device *pdev)
+{
+ struct arcotg_udc *udc =
+ (struct arcotg_udc *)platform_get_drvdata(pdev);
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ DECLARE_COMPLETION(done);
+
+ if (!udc)
+ return -ENODEV;
+
+ udc->done = &done;
+
+ if (udc->transceiver) {
+ put_device(udc->transceiver->dev);
+ udc->transceiver = 0;
+ }
+
+ /* DR has been stopped in usb_gadget_unregister_driver() */
+
+ /* remove proc */
+ remove_proc_file();
+
+ /* free irq */
+ free_irq(pdev->resource[1].start, udc);
+
+ /* deinitialize all ep: strcut */
+ /* deinitialize ep0: reg and QH */
+
+ /* Free allocated memory */
+ pr_debug("status_req->head = 0x%p\n", udc->status_req->head);
+ if (udc->status_req->head) {
+ pr_debug("freeing head=0x%p\n", udc->status_req->head);
+ dma_pool_free(udc->dtd_pool,
+ udc->status_req->head,
+ udc->status_req->head->td_dma);
+ }
+
+ kfree(udc->status_req->req.buf);
+ kfree(udc->status_req);
+
+ if (udc->dtd_pool)
+ dma_pool_destroy(udc->dtd_pool);
+
+ device_unregister(&udc->gadget.dev);
+ /* free udc --wait for the release() finished */
+ wait_for_completion(&done);
+
+#if 0 /* DDD */
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+#endif
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, etc.
+ */
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+
+ return 0;
+}
+
+static int udc_suspend(struct arcotg_udc *udc)
+{
+ udc->stopped = 1;
+ return 0;
+}
+
+/*!
+ * Modify Power management attributes
+ * Here we stop the DR controller and disable the irq
+ * @param dev device controller pointer
+ * @param state current state
+ * @return The function returns 0 on success or -1 if failed
+ */
+static int arcotg_udc_suspend(struct device *dev, pm_message_t state)
+{
+ struct arcotg_udc *udc = (struct arcotg_udc *)dev_get_drvdata(dev);
+ pr_debug("udc: Suspend. state=%d\n", state.event);
+ return udc_suspend(udc);
+}
+
+static int udc_resume(struct arcotg_udc *udc)
+{
+ /*Enable DR irq reg and set controller Run */
+ if (udc->stopped) {
+ dr_controller_setup(udc);
+ dr_controller_run(udc);
+ }
+ udc->usb_state = USB_STATE_ATTACHED;
+ udc->ep0_state = WAIT_FOR_SETUP;
+ udc->ep0_dir = 0;
+ return 0;
+}
+
+/*!
+ * Invoked on USB resume. May be called in_interrupt.
+ * Here we start the DR controller and enable the irq
+ * @param dev device controller pointer
+ * @return The function returns 0 on success or -1 if failed
+ */
+static int arcotg_udc_resume(struct device *dev)
+{
+ struct arcotg_udc *udc = (struct arcotg_udc *)dev_get_drvdata(dev);
+ pr_debug("udc: Resume dev=0x%p udc=0x%p\n", dev, udc);
+
+ return udc_resume(udc);
+}
+
+/*!
+ * Register entry point for the peripheral controller driver
+ */
+static struct platform_driver udc_driver = {
+ .probe = arcotg_udc_probe,
+ .remove = __exit_p(arcotg_udc_remove),
+ .driver = {
+ .name = driver_name,
+ },
+};
+
+static int __init udc_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO "%s version %s init \n", driver_desc, DRIVER_VERSION);
+ rc = platform_driver_register(&udc_driver);
+ pr_debug("udc: %s() driver_register returns %d\n", __FUNCTION__, rc);
+ return rc;
+}
+
+module_init(udc_init);
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&udc_driver);
+}
+
+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..fad34300b5ba
--- /dev/null
+++ b/drivers/usb/gadget/arcotg_udc.h
@@ -0,0 +1,598 @@
+/*
+ * 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 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 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[63];
+ 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 */
+} __attribute__ ((packed));
+
+/* 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
+
+/* 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)
+
+/* 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_RSV (0x00000001)
+#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)
+
+/*!
+ * 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_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_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)
+
+/* -----------------------------------------------------------------------*/
+/* ##### enum data
+*/
+typedef enum {
+ e_ULPI,
+ e_UTMI_8BIT,
+ e_UTMI_16BIT,
+ e_SERIAL
+} e_PhyInterface;
+
+/*-------------------------------------------------------------------------*/
+
+struct arcotg_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 arcotg_ep *ep;
+ unsigned mapped;
+
+ struct ep_td_struct *head, *tail; /* For dTD List
+ this is a BigEndian Virtual addr */
+ unsigned int dtd_count;
+};
+
+#define REQ_UNCOMPLETE (1)
+
+struct arcotg_ep {
+ struct usb_ep ep;
+ struct list_head queue;
+ struct arcotg_udc *udc;
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_gadget *gadget;
+
+ u8 already_seen;
+ u8 setup_stage;
+ u32 last_io; /* timestamp */
+
+ char name[14];
+#if 0
+ u16 maxpacket;
+ u8 bEndpointAddress;
+ u8 bmAttributes;
+#endif
+ unsigned double_buf:1;
+ unsigned stopped:1;
+ unsigned fnf:1;
+ unsigned has_dma:1;
+ u8 ackwait;
+ u8 dma_channel;
+ u16 dma_counter;
+ int lch;
+
+ struct timer_list timer;
+
+};
+
+#define EP_DIR_IN 1
+#define EP_DIR_OUT 0
+
+struct arcotg_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+ struct arcotg_ep eps[USB_MAX_ENDPOINTS * 2];
+ struct usb_ctrlrequest local_setup_buff;
+ spinlock_t lock;
+ struct fsl_usb2_platform_data *pdata;
+ u32 xcvr_type;
+ struct otg_transceiver *transceiver;
+ unsigned softconnect:1;
+ unsigned vbus_active:1;
+ unsigned stopped:1;
+
+ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
+ int ep_qh_size; /* Endpoints Queue-Head */
+ struct arcotg_req *status_req; /* ep0 status request */
+
+ 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_state; /* Enpoint zero state */
+ u32 ep0_dir; /* Enpoint zero direction: can be
+ USB_DIR_IN or USB_DIR_OUT */
+ u32 usb_sof_count; /* SOF count */
+ u32 errors; /* USB ERRORs count */
+ dma_addr_t ep_qh_dma; /* DMA address of ep_qh */
+ struct dma_pool *dtd_pool;
+ u8 device_address; /* Device USB address */
+
+ struct completion *done; /* to make sure release() is done */
+};
+
+#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
+
+/*-------------------------------------------------------------------------*/
+
+/* ### 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])
+
+/* Bulk only class request */
+#define USB_BULK_RESET_REQUEST 0xff
+
+#endif /* __ARCOTG_UDC_H */
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 9e732bff9df0..d786788b9c02 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -239,6 +239,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
+#ifdef CONFIG_USB_GADGET_ARC
+#define DEV_CONFIG_CDC
+#endif
+
#ifdef CONFIG_USB_GADGET_S3C2410
#define DEV_CONFIG_CDC
#endif
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index f7f159c1002b..579cd6e992c1 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -88,6 +88,12 @@
#define gadget_is_pxa27x(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_ARC
+#define gadget_is_arcotg(g) !strcmp("arc_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
@@ -212,5 +218,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x20;
else if (gadget_is_m66592(gadget))
return 0x21;
+ else if (gadget_is_arcotg(gadget))
+ return 0x22;
return -ENOENT;
}
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 49a91c5ee51b..35b34bf3908c 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -29,6 +29,52 @@ 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 ARC controller"
+ depends on USB_EHCI_HCD && ARCH_MXC
+ ---help---
+ Some Freescale processors have an ARC 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 ARC controller"
+ depends on USB_EHCI_ARC && (ARCH_MX27 || ARCH_MX3)
+ ---help---
+ Enable support for the USB Host1 port.
+
+config USB_EHCI_ARC_H2
+ bool "Support for Host2 port on ARC controller"
+ depends on USB_EHCI_ARC && (ARCH_MX27 || ARCH_MX3)
+ ---help---
+ Enable support for the USB Host2 port.
+
+config USB_EHCI_ARC_OTG
+ bool "Support for OTG host port on ARC controller"
+ depends on USB_EHCI_ARC
+ default y
+ ---help---
+ Enable support for the USB OTG port in HS/FS Host mode.
+
+choice
+ prompt "Select transceiver speed"
+ depends on USB_EHCI_ARC_OTG
+ default USB_EHCI_ARC_OTGHS
+ default USB_EHCI_ARC_OTGFS if ARCH_MX27
+
+config USB_EHCI_ARC_OTGHS
+ bool "High Speed"
+ ---help---
+ Enable support for the USB OTG port in HS Host mode.
+
+config USB_EHCI_ARC_OTGFS
+ bool "Full Speed"
+ ---help---
+ Enable support for the USB OTG port in FS Host mode.
+endchoice
+
config USB_EHCI_SPLIT_ISO
bool "Full speed ISO transactions (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
@@ -41,6 +87,7 @@ config USB_EHCI_SPLIT_ISO
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators (EXPERIMENTAL)"
depends on USB_EHCI_HCD && EXPERIMENTAL
+ 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..99d6772302de
--- /dev/null
+++ b/drivers/usb/host/ehci-arc.c
@@ -0,0 +1,393 @@
+/*
+ * drivers/usb/host/ehci-arc.c
+ *
+ * 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 USB ARC OTG USB Driver
+ */
+/*!
+ * @file ehci-arc.c
+ * @brief platform related part of usb host driver.
+ * @ingroup USB
+ */
+
+/*!
+ * Include files
+ */
+
+/* Note: this file is #included by ehci-hcd.c */
+
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/fsl_xcvr.h>
+#include <asm/arch/fsl_usb.h>
+
+#include "ehci-fsl.h"
+
+#undef dbg
+#undef vdbg
+
+#if 0
+#define dbg printk
+#else
+#define dbg(fmt, ...) do {} while (0)
+#endif
+
+#if 0
+#define vdbg dbg
+#else
+#define vdbg(fmt, ...) do {} while (0)
+#endif
+
+extern void fsl_platform_set_vbus_power(struct fsl_usb2_platform_data *pdata,
+ int on);
+
+/* 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;
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ pr_debug("initializing FSL-SOC USB Controller\n");
+
+ /* Need platform data for setup */
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "No platform data for %s.\n", pdev->dev.bus_id);
+ return -ENODEV;
+ }
+
+ retval = fsl_platform_verify(pdev);
+ if (retval)
+ return retval;
+
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->platform_init && pdata->platform_init(pdev)) {
+ retval = -ENODEV;
+ goto err1;
+ }
+
+ 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;
+
+ fsl_platform_set_vbus_power(pdata, 1);
+
+ hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->rsrc_start = pdata->r_start;
+ hcd->rsrc_len = pdata->r_len;
+ hcd->regs = pdata->regs;
+ vdbg("rsrc_start=0x%llx rsrc_len=0x%llx virtual=0x%x\n",
+ hcd->rsrc_start, hcd->rsrc_len, hcd->regs);
+
+ hcd->power_budget = pdata->power_budget;
+
+ /* DDD
+ * the following must be done by this point, otherwise the OTG
+ * host port doesn't make it thru initializtion.
+ * ehci_halt(), called by ehci_fsl_setup() returns -ETIMEDOUT
+ */
+ fsl_platform_set_host_mode(hcd);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval != 0) {
+ pr_debug("failed with usb_add_hcd\n");
+ goto err2;
+ }
+#if defined(CONFIG_USB_OTG)
+ if (pdata->does_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);
+ dev_dbg(ehci->transceiver->dev,
+ "init %s transceiver, retval %d\n",
+ ehci->transceiver->label, retval);
+ if (retval) {
+ if (ehci->transceiver)
+ put_device(ehci->transceiver->dev);
+ goto err2;
+ }
+ } else {
+ printk(KERN_ERR "can't find transceiver\n");
+ retval = -ENODEV;
+ goto err2;
+ }
+ }
+#endif
+
+ return retval;
+
+ 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;
+}
+
+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;
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ dbg("%s hcd=0x%p\n", __FUNCTION__, hcd);
+
+ /* DDD shouldn't we turn off the power here? */
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+
+ if (ehci->transceiver) {
+ (void)otg_set_host(ehci->transceiver, 0);
+ put_device(ehci->transceiver->dev);
+ }
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, etc.
+ */
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+}
+
+/* called after powerup, by probe or system-pm "wakeup" */
+static int ehci_fsl_reinit(struct ehci_hcd *ehci)
+{
+ fsl_platform_usb_setup(ehci_to_hcd(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;
+
+ /* EHCI registers start at offset 0x00 */
+ ehci->caps = hcd->regs + 0x100;
+ ehci->regs = hcd->regs + 0x100 +
+ HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+ vdbg("%s(): ehci->caps=0x%p ehci->regs=0x%p\n", __FUNCTION__,
+ ehci->caps, ehci->regs);
+
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->is_tdi_rh_tt = 1;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+
+ retval = ehci_fsl_reinit(ehci);
+ return retval;
+}
+
+/* *INDENT-OFF* */
+static const struct hc_driver ehci_arc_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,
+
+ /*
+ * 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,
+};
+/* *INDENT-ON* */
+
+#ifdef CONFIG_USB_OTG
+volatile static struct ehci_regs usb_ehci_regs;
+
+/* 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_arc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct fsl_usb2_platform_data *pdata =
+ (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+ u32 cmd;
+
+ dbg("%s pdev=0x%p pdata=0x%p ehci=0x%p hcd=0x%p\n",
+ __FUNCTION__, pdev, pdata, ehci, hcd);
+ dbg("%s ehci->regs=0x%p hcd->regs=0x%p hcd->state=%d\n",
+ __FUNCTION__, ehci->regs, hcd->regs, hcd->state);
+ dbg("%s pdata->usbmode=0x%x\n", __FUNCTION__, pdata->usbmode);
+
+ hcd->state = HC_STATE_HALT; /* ignore non-host interrupts */
+
+ cmd = readl(&ehci->regs->command);
+ cmd &= ~CMD_RUN;
+ writel(cmd, &ehci->regs->command);
+
+ memcpy((void *)&usb_ehci_regs, ehci->regs, sizeof(struct ehci_regs));
+ usb_ehci_regs.port_status[0] &=
+ cpu_to_le32(~(PORT_PEC | PORT_OCC | PORT_CSC));
+
+ fsl_platform_set_vbus_power(pdata, 0);
+
+ return 0;
+}
+
+static int ehci_arc_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ u32 cmd;
+ struct fsl_usb2_platform_data *pdata =
+ (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ dbg("%s pdev=0x%p pdata=0x%p ehci=0x%p hcd=0x%p\n",
+ __FUNCTION__, pdev, pdata, ehci, hcd);
+ vdbg("%s ehci->regs=0x%p hcd->regs=0x%p usbmode=0x%x\n",
+ __FUNCTION__, ehci->regs, hcd->regs, pdata->usbmode);
+
+ writel(USBMODE_CM_HOST, pdata->usbmode);
+ memcpy(ehci->regs, (void *)&usb_ehci_regs, sizeof(struct ehci_regs));
+
+ hcd->state = HC_STATE_RUNNING;
+
+ cmd = readl(&ehci->regs->command);
+ cmd |= CMD_RUN;
+ writel(cmd, &ehci->regs->command);
+
+ fsl_platform_set_vbus_power(pdata, 1);
+
+ return 0;
+}
+#endif /* CONFIG_USB_OTG */
+
+static int ehci_hcd_drv_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_hcd_fsl_probe(&ehci_arc_hc_driver, pdev);
+}
+
+static int __init_or_module ehci_hcd_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_hcd_fsl_remove(hcd, pdev);
+
+ return 0;
+}
+
+/* *INDENT-OFF* */
+static struct platform_driver ehci_fsl_driver = {
+ .probe = ehci_hcd_drv_probe,
+ .remove = ehci_hcd_drv_remove,
+#ifdef CONFIG_USB_OTG
+ .suspend = ehci_arc_suspend,
+ .resume = ehci_arc_resume,
+#endif
+ .driver = {
+ .name = "fsl-ehci",
+ },
+};
+/* *INDENT-ON* */
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 5f2d74ed5ad7..7bfd0bcc7bd0 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -954,6 +954,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.h b/drivers/usb/host/ehci.h
index 951d69fec513..8946ef4482a7 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -120,6 +120,12 @@ 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;
+
/* irq statistics */
#ifdef EHCI_STATS
struct ehci_stats stats;
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
new file mode 100644
index 000000000000..20ccb828b740
--- /dev/null
+++ b/drivers/usb/otg/Kconfig
@@ -0,0 +1,5 @@
+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.
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
new file mode 100644
index 000000000000..dc37de2c2360
--- /dev/null
+++ b/drivers/usb/otg/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for USB OTG controller driver
+#
+# USB transceiver
+fsl_otg_arc-objs := fsl_otg.o otg_fsm.o
+obj-$(CONFIG_TRANSCEIVER_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..1436fa1c6c4b
--- /dev/null
+++ b/drivers/usb/otg/fsl_otg.c
@@ -0,0 +1,1078 @@
+/*
+ * 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
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/platform_device.h>
+#include <linux/usb_gadget.h>
+#include <linux/time.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <linux/fsl_devices.h>
+#include "fsl_otg.h"
+#include <asm/arch/arc_otg.h>
+
+#define CONFIG_USB_OTG_DEBUG_FILES
+#define DRIVER_VERSION "Revision: 1.0"
+#define DRIVER_AUTHOR "Jerry Huang/Leo Li"
+#define DRIVER_DESC "USB OTG Driver"
+#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
+
+MODULE_DESCRIPTION("ARC USB OTG Transceiver Driver");
+
+static const char otg_dr_name[] = "fsl_arc";
+static spinlock_t usb_dr_regs_lock;
+
+#undef HA_DATA_PULSE
+
+volatile static struct usb_dr_mmap *usb_dr_regs;
+static struct fsl_otg *fsl_otg_dev = NULL;
+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,
+};
+
+/**
+ * usb_bus_start_enum - start immediate enumeration (for OTG)
+ * @bus: the bus (must use hcd framework)
+ * @port: 1-based number of port; usually bus->otg_port
+ * Context: in_interrupt()
+ *
+ * Starts enumeration, with an immediate reset followed later by
+ * khubd identifying and possibly configuring the device.
+ * This is needed by OTG controller drivers, where it helps meet
+ * HNP protocol timing requirements for starting a port reset.
+ */
+
+#include "../../../drivers/usb/core/hcd.h"
+
+int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num)
+{
+ struct usb_hcd *hcd;
+ int status = -EOPNOTSUPP;
+
+ /* NOTE: since HNP can't start by grabbing the bus's address0_sem,
+ * boards with root hubs hooked up to internal devices (instead of
+ * just the OTG port) may need more attention to resetting...
+ */
+
+ hcd = container_of(bus, struct usb_hcd, self);
+ if (port_num && hcd->driver->start_port_reset)
+ status = hcd->driver->start_port_reset(hcd, port_num);
+
+ /* run khubd shortly after (first) root port reset finishes;
+ * it may issue others, until at least 50 msecs have passed.
+ */
+ if (status == 0)
+ mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(10));
+
+ return status;
+}
+
+#if defined(CONFIG_ISP1504_MXC)
+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;
+}
+#endif
+
+/* 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)
+{
+}
+
+/* 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;
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb_dr_regs_lock, flags);
+ command = readl(&usb_dr_regs->usbcmd);
+ command |= UCMD_RESET;
+ writel(command, &usb_dr_regs->usbcmd);
+ spin_unlock_irqrestore(&usb_dr_regs_lock, flags);
+ while (readl(&usb_dr_regs->usbcmd) & UCMD_RESET)
+ continue;
+}
+
+/* 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;
+ pm_message_t state = { 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......");
+ 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......");
+ if (dev && dev->driver) {
+ retval = dev->driver->suspend(dev, 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 *udc_dev;
+ pm_message_t state = { 0 };
+
+ if (!xceiv->gadget || !xceiv->gadget->dev.parent)
+ return -ENODEV;
+
+ VDBG("gadget %s", on ? "on" : "off");
+ udc_dev = xceiv->gadget->dev.parent;
+
+ if (on)
+ udc_dev->driver->resume(udc_dev);
+ else
+ udc_dev->driver->suspend(udc_dev, 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);
+ struct device *dev;
+ pm_message_t state = { 0 };
+
+ 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;
+ dev = host->controller;
+
+ if (dev && dev->driver)
+ dev->driver->suspend(dev, state);
+ } 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", (int)otg_dev);
+ VDBG("fsl_otg_dev 0x%x", (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", 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("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 delayed_work *dwork =
+ container_of(work, struct delayed_work, work);
+ struct fsl_otg *otg = container_of(dwork, struct fsl_otg, otg_event);
+ struct otg_fsm *fsm = &otg->fsm;
+
+ if (fsm->id) { /* switch to gadget */
+ fsl_otg_start_host(fsm, 0);
+ otg_drv_vbus(fsm, 0);
+ fsl_otg_start_gadget(fsm, 1);
+ }
+}
+
+/* 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_IS_USB_ID) {
+ fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0;
+ otg->default_a = (fsm->id == 0);
+ if (otg->host)
+ otg->host->is_b_host = fsm->id;
+ if (otg->gadget)
+ otg->gadget->is_a_peripheral = !fsm->id;
+ VDBG("IRQ=ID now=%d", fsm->id);
+
+ if (fsm->id) { /* switch to gadget */
+ schedule_delayed_work((struct delayed_work *)
+ &((struct fsl_otg *)
+ dev_id)->otg_event, 25);
+ } else { /* switch to host */
+ cancel_delayed_work((struct 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 = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ DBG();
+
+ if (fsl_otg_dev)
+ return 0;
+
+ /* allocate space to fsl otg device */
+ fsl_otg_tc = kmalloc(sizeof(struct fsl_otg), GFP_KERNEL);
+ if (!fsl_otg_tc)
+ return -ENODEV;
+
+ memset(fsl_otg_tc, 0, sizeof(struct fsl_otg));
+
+ fsl_otg_tc->dr_mem_map = pdata->regs;
+
+ DBG("set dr_mem_map to 0x%p", pdata->regs);
+ spin_lock_init(&usb_dr_regs_lock);
+
+ 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;
+ }
+
+ /* Set OTG state machine operations */
+ fsl_otg_tc->fsm.ops = &fsl_otg_ops;
+
+ /* record initial state of ID pin */
+ fsl_otg_tc->fsm.id = (fsl_otg_tc->dr_mem_map->otgsc & OTGSC_STS_USB_ID)
+ ? 1 : 0;
+ DBG("initial ID pin=%d", fsl_otg_tc->fsm.id);
+
+ /* 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_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;
+ int status;
+ u32 temp;
+ struct resource *res;
+ unsigned long flags;
+
+ DBG();
+
+ 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;
+
+ usb_dr_regs = p_otg->dr_mem_map;
+ DBG("set usb_dr_regs to 0x%p", usb_dr_regs);
+
+ /* request irq */
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Can't find irq resource.\n");
+ return -ENODEV;
+ }
+ p_otg->irq = res->start;
+ DBG("requesting irq %d", p_otg->irq);
+ status =
+ request_irq(p_otg->irq, fsl_otg_isr, IRQF_SHARED, "fsl_arc", p_otg);
+ if (status) {
+ dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n",
+ p_otg->irq, status);
+ kfree(p_otg);
+ return status;
+ }
+
+ /*
+ * The ID input is FALSE when a Mini-A plug is inserted
+ * in the Mini-AB receptacle. Otherwise, this input is TRUE.
+ */
+ if (le32_to_cpu(p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID)
+ p_otg->otg.state = OTG_STATE_UNDEFINED; /* not Mini-A */
+ else
+ p_otg->otg.state = OTG_STATE_A_IDLE; /* Mini-A */
+
+ /* enable OTG interrupt */
+ spin_lock_irqsave(&usb_dr_regs_lock, flags);
+ temp = readl(&p_otg->dr_mem_map->otgsc);
+
+ temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
+ temp |= OTGSC_IE_USB_ID;
+ writel(temp, &p_otg->dr_mem_map->otgsc);
+ spin_unlock_irqrestore(&usb_dr_regs_lock, flags);
+
+ return 0;
+}
+
+static int board_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata;
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->platform_init(pdev) != 0)
+ return -EINVAL;
+
+ 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" "isp1504_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: %x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->portsc);
+ t = scnprintf(next, size, "PORTSC reg: %x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbmode);
+ t = scnprintf(next, size, "USBMODE reg: %x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbcmd);
+ t = scnprintf(next, size, "USBCMD reg: %x\n", tmp_reg);
+ size -= t;
+ next += t;
+
+ tmp_reg = le32_to_cpu(usb_dr_regs->usbsts);
+ t = scnprintf(next, size, "USBSTS reg: %x\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;
+
+#ifdef 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 */
+
+static int __init fsl_otg_probe(struct platform_device *pdev)
+{
+ int status;
+
+ DBG("pdev=0x%p", pdev);
+
+ if (!pdev)
+ return -ENODEV;
+
+ /* Initialize the clock, multiplexing pin and PHY interface */
+ board_init(pdev);
+
+ /* 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);
+
+ create_proc_file();
+ return status;
+}
+
+static int __exit fsl_otg_remove(struct platform_device *pdev)
+{
+ u32 ie;
+ struct fsl_usb2_platform_data *pdata;
+ unsigned long flags;
+
+ pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
+
+ DBG("pdev=0x%p pdata=0x%p", pdev, pdata);
+
+ otg_set_transceiver(NULL);
+
+ /* disable and clear OTGSC interrupts */
+ spin_lock_irqsave(&usb_dr_regs_lock, flags);
+ ie = readl(&usb_dr_regs->otgsc);
+ ie &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK;
+ ie |= OTGSC_INTERRUPT_STATUS_BITS_MASK;
+ writel(ie, &usb_dr_regs->otgsc);
+ spin_unlock_irqrestore(&usb_dr_regs_lock, flags);
+
+ free_irq(fsl_otg_dev->irq, fsl_otg_dev);
+
+ kfree(fsl_otg_dev);
+
+ remove_proc_file();
+
+ if (pdata->platform_uninit)
+ pdata->platform_uninit(pdata);
+
+ fsl_otg_dev = NULL;
+ return 0;
+}
+
+struct platform_driver fsl_otg_driver = {
+ .probe = fsl_otg_probe,
+ .remove = fsl_otg_remove,
+ .driver = {
+ .name = "fsl_arc",
+ .owner = THIS_MODULE,
+ },
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init fsl_usb_otg_init(void)
+{
+ printk(KERN_INFO "driver %s, %s\n", otg_dr_name, DRIVER_VERSION);
+ return platform_driver_register(&fsl_otg_driver);
+}
+
+static void __exit fsl_usb_otg_exit(void)
+{
+ platform_driver_unregister(&fsl_otg_driver);
+}
+
+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..b5a8a4b0afa5
--- /dev/null
+++ b/drivers/usb/otg/fsl_otg.h
@@ -0,0 +1,239 @@
+/*
+ * 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
+ */
+
+#include "otg_fsm.h"
+#include <linux/usb/otg.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)
+
+/* 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)
+
+/*
+ * 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];
+ 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 */
+ u8 host_working;
+ u8 on_off;
+
+ int irq;
+};
+
+struct fsl_otg_config {
+ u8 otg_port;
+};
+
+extern const char *state_string(enum usb_otg_state state);
+extern int otg_set_resources(struct resource *resources, int num);
diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c
new file mode 100644
index 000000000000..43e5af105441
--- /dev/null
+++ b/drivers/usb/otg/otg_fsm.c
@@ -0,0 +1,394 @@
+/*
+ * Copyright 2006-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 <asm/types.h>
+#include <linux/kernel.h>
+#include <linux/usb/otg.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/usb.h>
+#include <linux/usb_gadget.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";
+ }
+}
+
+const char *protocol_string(int p)
+{
+ switch (p) {
+ case PROTO_HOST:
+ return "Host";
+ case PROTO_GADGET:
+ return "Peripheral";
+ default:
+ return "undef";
+ }
+}
+
+/* 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("Change role from %s to %s",
+ protocol_string(fsm->protocol), protocol_string(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 = 0;
+
+/* 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("chg state to %s", 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 */
+
+ VDBG("top: curr state=%s", state_string(state));
+
+ switch (state) {
+ case OTG_STATE_UNDEFINED:
+ VDBG("fsm->id = %d", 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:
+ VDBG("gadget: %p", fsm->transceiver->gadget);
+ 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);
+
+ 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..5b1c98f16be9
--- /dev/null
+++ b/drivers/usb/otg/otg_fsm.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2006-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
+ */
+
+#if 0
+#define DEBUG 1
+#define VERBOSE 1
+#endif
+
+#ifdef DEBUG
+
+/*
+#define DBG(fmt, args...) printk("[%s] " fmt "\n", \
+ __FUNCTION__, ## args)
+*/
+#define DBG(fmt, args...) printk("j=%lu [%s] " fmt "\n", \
+ jiffies, __FUNCTION__, ## 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/usb/usblan/Kconfig b/drivers/usb/usblan/Kconfig
new file mode 100644
index 000000000000..cdf4d1c45d63
--- /dev/null
+++ b/drivers/usb/usblan/Kconfig
@@ -0,0 +1,24 @@
+#
+# USB Network devices configuration
+#
+comment "Belcarra USBLAN Networking for USB"
+ depends on USB && NET
+
+config USB_USBLAN
+ tristate "Support for Belcarra USBLAN Network Devices"
+ depends on USB && NET
+
+config USB_USBLAN_IDS
+ bool "Override built-in Vendor and Product ID's"
+ depends on USB && NET && USB_USBLAN
+ default "n"
+
+config USB_USBLAN_VENDORID
+ hex "USB Vendor ID for the USBLAN network device"
+ depends on USB_USBLAN && USB_USBLAN_IDS
+ default "0"
+
+config USB_USBLAN_PRODUCTID
+ hex "USB Product ID for the USBLAN network device"
+ depends on USB_USBLAN && USB_USBLAN_IDS
+ default "0"
diff --git a/drivers/usb/usblan/Makefile b/drivers/usb/usblan/Makefile
new file mode 100644
index 000000000000..969d96804c50
--- /dev/null
+++ b/drivers/usb/usblan/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for USB Network drivers
+#
+
+OTGDIR=$(srctree)/drivers/otg
+EXTRA_CFLAGS += -I$(OTGDIR)
+EXTRA_CFLAGS_nostdinc += -I$(OTGDIR)
+obj-$(CONFIG_USB_USBLAN) += usblan.o
diff --git a/drivers/usb/usblan/usblan-compat.h b/drivers/usb/usblan/usblan-compat.h
new file mode 100644
index 000000000000..b02eba4056d9
--- /dev/null
+++ b/drivers/usb/usblan/usblan-compat.h
@@ -0,0 +1,286 @@
+/*********************************************************************
+ * A set of macros to cross-navigate Linux 2.4 and Linux 2.6 kernel
+ * facilities.
+ * Copyright (c) 2004, 2005 Belcarra Technologies Corp
+ *
+ * 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.
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>, Bruce Balden <balden@belcarra.com>
+ *
+ ***********************************************************************/
+#ifndef _USB_NET_USBLAN_COMPAT_H
+#define _USB_NET_USBLAN_COMPAT_H 1
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
+#define LINUX26
+#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5)
+#define LINUX24
+#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */
+#define LINUX24
+#define LINUX_OLD
+#warning "Early unsupported release of Linux kernel"
+#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,4,5) */
+
+
+/*! @{ */
+#undef PRAGMAPACK
+#define PACKED __attribute__((packed))
+#define INLINE __inline__
+
+/*! @} */
+
+/*! @name Memory Allocation Primitives
+ *
+ * CKMALLOC()
+ * LSTRDUP()
+ * LKFREE()
+ * LIST_ENTRY()
+ * LIST_FOR_EACH()
+ */
+
+ /*! @{ */
+
+#if defined(LINUX26)
+ #include <linux/gfp.h>
+#define GET_KERNEL_PAGE() __get_free_page(GFP_KERNEL)
+
+#else /* LINUX26 */
+
+ #include <linux/mm.h>
+ #define GET_KERNEL_PAGE() get_free_page(GFP_KERNEL)
+#endif /* LINUX26 */
+
+
+
+// Common to all supported versions of Linux ??
+
+#define CKMALLOC(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f)
+#define LSTRDUP(str) _lstrdup(__FUNCTION__, __LINE__, str)
+#define LKFREE(p) _lkfree(__FUNCTION__, __LINE__, p)
+
+#define ckmalloc(n,f) _ckmalloc(__FUNCTION__, __LINE__, n, f)
+#define lstrdup(str) _lstrdup(__FUNCTION__, __LINE__, str)
+#define lkfree(p) _lkfree(__FUNCTION__, __LINE__, p)
+
+#define OTG_MALLOC_TEST
+#undef OTG_MALLOC_DEBUG
+
+#ifdef OTG_MALLOC_TEST
+ extern int otg_mallocs;
+#endif
+
+
+static INLINE void *_ckmalloc (const char *func, int line, int n, int f)
+{
+ void *p;
+ if ((p = kmalloc (n, f)) == NULL) {
+ return NULL;
+ }
+ memset (p, 0, n);
+ #ifdef OTG_MALLOC_TEST
+ ++otg_mallocs;
+ #endif
+ #ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs);
+ #endif
+ return p;
+}
+
+static INLINE char *_lstrdup (const char *func, int line, char *str)
+{
+ int n;
+ char *s;
+ if (str && (n = strlen (str) + 1) && (s = kmalloc (n, GFP_ATOMIC))) {
+#ifdef OTG_MALLOC_TEST
+ ++otg_mallocs;
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, s, func, line, otg_mallocs);
+#endif
+ return strcpy (s, str);
+ }
+ return NULL;
+}
+
+static INLINE void _lkfree (const char *func, int line, void *p)
+{
+ if (p) {
+#ifdef OTG_MALLOC_TEST
+ --otg_mallocs;
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ printk(KERN_INFO"%s: %p %s %d %d\n", __FUNCTION__, p, func, line, otg_mallocs);
+#endif
+ kfree (p);
+#ifdef MALLOC_TEST
+ if (otg_mallocs < 0) {
+ printk(KERN_INFO"%s: %p %s %d %d otg_mallocs less zero!\n", __FUNCTION__, p, func, line, otg_mallocs);
+ }
+#endif
+#ifdef OTG_MALLOC_DEBUG
+ else {
+ printk(KERN_INFO"%s: %s %d NULL\n", __FUNCTION__, func, line);
+ }
+#endif
+ }
+}
+
+#if 1
+#include <linux/list.h>
+#define LIST_NODE struct list_head
+#define LIST_NODE_INIT(name) struct list_head name = {&name, &name}
+#define INIT_LIST_NODE(ptr) INIT_LIST_HEAD(ptr)
+#define LIST_ENTRY(pointer, type, member) list_entry(pointer, type, member)
+#define LIST_FOR_EACH(cursor, head) list_for_each(cursor, head)
+#define LIST_ADD_TAIL(n,h) list_add_tail(n,h)
+#define LIST_DEL(h) list_del(&(h))
+#else
+#include <otg/otg-list.h>
+#endif
+
+/*! @} */
+
+
+/*! @name Atomic Operations
+ *
+ * atomic_post_inc()
+ * atomic_pre_dec()
+ */
+/*! @{ */
+static __inline__ int atomic_post_inc(volatile atomic_t *v)
+{
+ unsigned long flags;
+ int result;
+ local_irq_save(flags);
+ result = (v->counter)++;
+ local_irq_restore(flags);
+ return(result);
+}
+
+static __inline__ int atomic_pre_dec(volatile atomic_t *v)
+{
+ unsigned long flags;
+ int result;
+ local_irq_save(flags);
+ result = --(v->counter);
+ local_irq_restore(flags);
+ return(result);
+}
+/*! @} */
+
+
+
+/*!@name Scheduling Primitives
+ *
+ * WORK_STRUCT
+ * WORK_ITEM
+ *
+ * SCHEDULE_TIMEOUT()\n
+ * SET_WORK_ARG()\n
+ * SCHEDULE_WORK()\n
+ * SCHEDULE_IMMEDIATE_WORK()\n
+ * NO_WORK_DATA()\n
+ * MOD_DEC_USE_COUNT\n
+ * MOD_INC_USE_COUNT\n
+ */
+
+/*! @{ */
+
+static void inline SCHEDULE_TIMEOUT(int seconds){
+ schedule_timeout( seconds * HZ );
+}
+
+
+/* Separate Linux 2.4 and 2.6 versions of scheduling primitives */
+#if defined(LINUX26)
+ #include <linux/workqueue.h>
+
+ #define WORK_STRUCT work_struct
+ #define WORK_ITEM work_struct
+ typedef struct WORK_ITEM WORK_ITEM;
+ #if 0
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) INIT_WORK((__item),(__routine),(__data))
+ #else
+ #include <linux/interrupt.h>
+ #define PREPARE_WORK_ITEM(__item,__routine,__data) __prepare_work(&(__item),(__routine),(__data))
+ static inline void __prepare_work(struct work_struct *_work,
+ void (*_routine),
+ void * _data){
+ INIT_LIST_HEAD(&_work->entry);
+ _work->pending = 0;
+ _work->func = _routine;
+ _work->data = _data;
+ init_timer(&_work->timer);
+ }
+ #endif
+ #undef PREPARE_WORK
+ typedef void (* WORK_PROC)(void *);
+
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+
+ #define SCHEDULE_WORK(item) schedule_work(&(item))
+ #define SCHEDULE_IMMEDIATE_WORK(item) SCHEDULE_WORK((item))
+ #define PENDING_WORK_ITEM(item) ((item).pending != 0)
+ #define NO_WORK_DATA(item) (!(item).data)
+ #define _MOD_DEC_USE_COUNT //Not used in 2.6
+ #define _MOD_INC_USE_COUNT //Not used in 2.6
+
+#else /* LINUX26 */
+
+ #define WORK_STRUCT tq_struct
+ #define WORK_ITEM tq_struct
+ typedef struct WORK_ITEM WORK_ITEM;
+ #define PREPARE_WORK_ITEM(item,work_routine,work_data) { item.routine = work_routine; item.data = work_data; }
+ #define SET_WORK_ARG(__item, __data) (__item).data = __data
+ #define NO_WORK_DATA(item) (!(item).data)
+ #define SCHEDULE_WORK(item) schedule_task(&(item))
+ #define PENDING_WORK_ITEM(item) ((item).sync != 0)
+ #define _MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT
+ #define _MOD_INC_USE_COUNT MOD_INC_USE_COUNT
+
+ typedef void (* WORK_PROC)(void *);
+
+ #if !defined(IRQ_HANDLED)
+ // Irq's
+ typedef void irqreturn_t;
+ #define IRQ_NONE
+ #define IRQ_HANDLED
+ #define IRQ_RETVAL(x)
+ #endif
+#endif /* LINUX26 */
+
+/*! @} */
+
+/*!@name Semaphores
+ *
+ * up()
+ * down()
+ */
+/*! @{ */
+#define UP(s) up(s)
+#define DOWN(s) down(s)
+/*! @} */
+
+/*! @name Printk
+ *
+ * PRINTK()
+ */
+/*! @{ */
+#define PRINTK(s) printk(s)
+/*! @} */
+
+/*!
+ * Cache
+ * @{
+ */
+#define CACHE_SYNC_RCV(buf, len) pci_map_single (NULL, (void *) buf, len, PCI_DMA_FROMDEVICE)
+#define CACHE_SYNC_TX(buf, len) consistent_sync (buf, len, PCI_DMA_TODEVICE)
+
+/* @} */
+
+
+
+#endif
diff --git a/drivers/usb/usblan/usblan.c b/drivers/usb/usblan/usblan.c
new file mode 100644
index 000000000000..f59c094ac015
--- /dev/null
+++ b/drivers/usb/usblan/usblan.c
@@ -0,0 +1,3045 @@
+/*
+ * USB Host to USB Device Network Function Driver
+ *
+ * Copyright (c) 2002, 2003 Belcarra
+ * Copyright (c) 2001 Lineo
+ *
+ *
+ * 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.
+ *
+ * By:
+ * Stuart Lynne <sl@belcarra.com>, Bruce Balden <balden@belcarra.com>
+ *
+ * Some algorithms adopted from usbnet.c:
+ *
+ * Copyright (C) 2000-2001 by David Brownell <dbrownell@users.sourceforge.net>
+ *
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+//#include <net/arp.h>
+#include <linux/rtnetlink.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/pkt_sched.h>
+#include <linux/random.h>
+#include <linux/version.h>
+
+#include <linux/skbuff.h>
+#include <net/arp.h>
+#include <linux/atmdev.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/inetdevice.h>
+#include <linux/workqueue.h>
+
+
+
+//#include "linux-pch.h"
+
+#include <linux/usb.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include "usblan-compat.h"
+#include "usblan.h"
+
+#define PACKED_ENUM enum
+#define PACKED_ENUM_EXTRA
+#define PACKED1 __attribute__((packed))
+#define PACKED2
+#define PACKED0
+
+
+#if defined(USBLAN_LOCAL_CONFIG)
+#include "./usblan-config.h"
+#endif
+
+#define MIN(a,b) (((a) < (b))?(a):(b))
+#define MAX(a,b) (((a) > (b))?(a):(b))
+
+#define UNLESS(x) if (!(x))
+#define THROW(x) goto x
+#define CATCH(x) while(0) x:
+#define THROW_IF(e, x) if (e) { goto x; }
+#define THROW_UNLESS(e, x) UNLESS (e) { goto x; }
+#define BREAK_IF(x) if (x) { break; }
+#define CONTINUE_IF(x) if (x) { continue; }
+#define RETURN_IF(x,y) if (y) { return x; }
+
+
+#define mutex_lock(x) down(x)
+#define mutex_unlock(x) up(x)
+
+
+#define DRIVER_VERSION "2.0.0"
+#define DRIVER_AUTHOR "sl@belcarra.com"
+#define DRIVER_DESC "Linux USBLAN driver"
+
+#ifdef MODULE
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+// XXX This needs to be corrected down to the last version of the
+// kernel that did NOT have MODULE_LICENSE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,15)
+MODULE_LICENSE("GPL");
+#endif
+#endif
+
+#define STATIC
+
+/* Module Parameters ************************************************************************* */
+
+#define MAX_INTERFACES 1
+
+#define MAX_RCV_SKBS 10
+#define TIMEOUT_JIFFIES (4*HZ)
+#define MAX_PACKET 32768
+#define MIN_PACKET sizeof(struct ethhdr)
+
+
+#define TX_QLEN 2
+#define RX_QLEN 2
+
+static DECLARE_MUTEX(usbd_mutex); // lock for global changes
+static LIST_HEAD(usbd_list); // a list for all active devices
+
+
+typedef enum usbdnet_device_type {
+ usbdnet_unknown, usbdnet_basic, usbdnet_cdc, usbdnet_safe, usbdnet_blan, usbdnet_rndis
+} usbdnet_device_type_t;
+
+char * usbdnet_device_names[] = {
+ "UNKNOWN", "BASIC", "CDC", "SAFE", "BLAN", "RNDIS",
+};
+
+
+
+__u8 SAFE_VERSION[2] = {
+ 0x00, 0x01, /* BCD Version */
+};
+__u8 SAFE_GUID[16] = {
+ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6, /* bGUID */
+ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f, /* bGUID */
+};
+
+__u8 BLAN_VERSION[2] = {
+ 0x00, 0x01, /* BCD Version */
+};
+__u8 BLAN_GUID[16] = {
+ 0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70, /* bGUID */
+ 0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37, /* bGUID */
+};
+
+// data detail
+#define DATA_CRC 0x01
+#define DATA_PADBEFORE 0x02
+#define DATA_PADAFTER 0x04
+#define DATA_FERMAT 0x08
+
+// cdc notifications
+#define CDC_NOTIFICATION 0xa1
+#define CDC_NOTIFICATION_NETWORK 0x00
+#define CDC_NOTIFICATION_SPEEDCHANGE 0x2a
+
+#define USB_DT_CS_INTERFACE 0x24
+
+#define MDLM_FUNCTIONAL 0x12
+#define MDLM_DETAIL 0x13
+
+#define MDLM_SAFE_GUID 0x00
+#define MDLM_BLAN_GUID 0x01
+
+
+#if 0
+STATIC void usblan_test_kalloc(char *msg)
+{
+ struct urb *foo;
+ printk(KERN_INFO "%s: %s\n",__FUNCTION__,msg);
+ if (NULL != (foo = USB_ALLOC_URB(0,GFP_ATOMIC))) {
+ usb_free_urb(foo);
+ }
+ printk(KERN_INFO "%s: #%08x\n",__FUNCTION__,(u32)(void*)foo);
+}
+#endif
+
+
+
+/* struct private
+ *
+ * This structure contains the network interface and additional per USB device information.
+ *
+ * A pointer to this structure is used in two three places:
+ *
+ * net->priv
+ * urb->context
+ * skb->cb.priv
+ */
+struct private {
+
+ // general
+ struct usb_device *usbdev; // usb core layer provides this for the probed device
+ struct semaphore mutex; // lock for changes to this structure
+ struct list_head list; // to maintain a list of these devices ()
+ wait_queue_head_t *wait;
+ wait_queue_head_t *ctrl_wait;
+
+ struct tasklet_struct bh;
+ struct WORK_STRUCT crc_task;
+ struct WORK_STRUCT ctrl_task;
+ struct WORK_STRUCT reset_task;
+ struct WORK_STRUCT unlink_task;
+
+ int intf_count;
+ int intf_max;
+
+ // network
+ struct net_device net;
+ struct net_device_stats stats;
+ unsigned char dev_addr[ETH_ALEN];
+
+ // queues
+ struct sk_buff_head rxq;
+ struct sk_buff_head txq;
+ struct sk_buff_head unlink;
+ struct sk_buff_head done;
+
+ //
+ int crc32; // append and check for appended 32bit CRC
+ int padded; // pad bulk transfers such that (urb->transfer_buffer_length % data_ep_out_size) == 1
+ int addr_set; // set_address
+ int sawCRC;
+
+ int timeouts;
+
+ usbdnet_device_type_t usbdnet_device_type;
+
+ //struct usb_interface *data_interface;
+ //struct usb_interface *comm_interface;
+
+ struct usb_ctrlrequest ctrl_request;
+ struct urb *ctrl_urb;
+ int configuration_number;
+ int bConfigurationValue;
+
+ int comm_interface;
+ int comm_bInterfaceNumber;
+ int comm_bAlternateSetting;
+ int comm_ep_in;
+ int comm_ep_in_size;
+
+ int data_interface;
+
+ int data_bInterfaceNumber;
+ int data_bAlternateSetting;
+
+ int nodata_bInterfaceNumber;
+ int nodata_bAlternateSetting;
+
+ int data_ep_in;
+ int data_ep_out;
+ int data_ep_in_size;
+ int data_ep_out_size;
+
+ u8 CRCInUse;
+ u8 CDC;
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+ u8 bPad;
+};
+
+/* struct skb_cb
+ *
+ * This defines how we use the skb->cb data area. It allows us to get back to the private
+ * data structure and track the current state of skb in our done queue. There is a pointer
+ * to the active urb so that it can be cancelled (e.g. tx_timeout).
+ *
+ * skb->cb
+ */
+typedef enum skb_state {
+ unknown = 0,
+ tx_start, // an skb in priv->txq
+ tx_done, // a transmitted skb in priv->done
+ rx_start, // an skb in priv->rxq
+ rx_done, // a received skb in priv->done
+ rx_cleanup, // a received skb being thrown out due to an error condition
+} skb_state_t;
+
+struct skb_cb {
+ struct private *priv;
+ struct urb *urb;
+ skb_state_t state;
+ unsigned long int jiffies;
+};
+
+
+static struct usb_driver usblan_driver;
+
+
+struct usb_mdlm_detail_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bGuidDescriptorType;
+} __attribute__ ((packed));
+
+struct usb_safe_detail_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bGuidDescriptorType;
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+} __attribute__ ((packed));
+
+struct usb_blan_detail_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bGuidDescriptorType;
+ u8 bmNetworkCapabilities;
+ u8 bmDataCapabilities;
+ u8 bPad;
+} __attribute__ ((packed));
+
+struct usb_class_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 data[0];
+} __attribute__ ((packed));
+
+struct usb_guid_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u8 bGuidDescriptorType;
+ u8 data[0];
+} __attribute__ ((packed));
+
+struct usb_notification_descriptor {
+ u8 bmRequestType;
+ u8 bNotification;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ u8 data[2];
+} __attribute__ ((packed));
+
+struct usb_speedchange_descriptor {
+ u8 bmRequestType;
+ u8 bNotification;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+ u32 data[2];
+} __attribute__ ((packed));
+
+struct usb_mdlm_functional_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u8 bDescriptorSubType;
+ u16 bcdVersion;
+ u8 bGuid[16];
+} __attribute__ ((packed));
+
+
+
+/*
+ * default MAC address to use
+ */
+static unsigned char default_addr[ETH_ALEN];
+
+/* Module Parameters ************************************************************************* */
+
+#define VENDOR_SPECIFIC_CLASS 0xff
+#define VENDOR_SPECIFIC_SUBCLASS 0xff
+#define VENDOR_SPECIFIC_PROTOCOL 0xff
+
+#define MTU 1500+100
+
+
+#if defined(CONFIG_USBD_USBLAN_VENDOR) && !defined(CONFIG_USBD_USBLAN_PRODUCT)
+#abort "USBLAN_VENDOR defined without USBLAN_PRODUCT"
+#endif
+
+#define CDC_DEVICE_CLASS 0x02 // Device descriptor Class
+
+#define CDC_INTERFACE_CLASS 0x02 // CDC interface descriptor Class
+#define CDC_INTERFACE_SUBCLASS 0x06 // CDC interface descriptor SubClass
+#define RNDIS_INTERFACE_SUBCLASS 0x02 // CDC interface descriptor SubClass
+#define MDLM_INTERFACE_SUBCLASS 0x0a // CDC interface descriptor SubClass
+
+#define DATA_INTERFACE_CLASS 0x0a // Data interface descriptor Class
+
+#define DEFAULT_PROTOCOL 0x00
+#define VENDOR_SPECIFIC_PROTOCOL 0xff
+
+#define LINEO_INTERFACE_CLASS 0xff // Lineo private interface descriptor Class
+
+#define LINEO_INTERFACE_SUBCLASS_SAFENET 0x01 // Lineo private interface descriptor SubClass
+#define LINEO_INTERFACE_SUBCLASS_SAFESERIAL 0x02
+
+#define LINEO_SAFENET_CRC 0x01 // Lineo private interface descriptor Protocol
+#define LINEO_SAFENET_CRC_PADDED 0x02 // Lineo private interface descriptor Protocol
+
+#define LINEO_SAFESERIAL_CRC 0x01
+#define LINEO_SAFESERIAL_CRC_PADDED 0x02
+
+
+#define NETWORK_ADDR_HOST 0xac100005 /* 172.16.0.0 */
+#define NETWORK_ADDR_CLIENT 0xac100006 /* 172.16.0.0 */
+#define NETWORK_MASK 0xfffffffc
+
+
+static __u32 vendor_id; // no default
+static __u32 product_id; // no default
+static __u32 class = LINEO_INTERFACE_CLASS;
+static __u32 subclass = LINEO_INTERFACE_SUBCLASS_SAFENET;
+
+static __u32 echo_fcs; // no default
+static __u32 noisy_fcs; // no default
+static __u32 echo_rx; // no default
+static __u32 echo_tx; // no default
+
+#ifdef MODULE
+MODULE_PARM_DESC(vendor_id, "User specified USB idVendor");
+MODULE_PARM_DESC(product_id, "User specified USB idProduct");
+MODULE_PARM_DESC(class, "User specified USB Class");
+MODULE_PARM_DESC(subclass, "User specified USB SubClass");
+MODULE_PARM(vendor_id, "i");
+MODULE_PARM(product_id, "i");
+MODULE_PARM(class, "i");
+MODULE_PARM(subclass, "i");
+
+MODULE_PARM_DESC(echo_tx, "echo TX urbs");
+MODULE_PARM_DESC(echo_rx, "echo RCV urbs");
+MODULE_PARM_DESC(echo_fcs, "BAD FCS");
+MODULE_PARM_DESC(noisy_fcs, "BAD FCS info");
+MODULE_PARM(echo_tx, "i");
+MODULE_PARM(echo_rx, "i");
+MODULE_PARM(echo_fcs, "i");
+MODULE_PARM(noisy_fcs, "i");
+#endif
+
+//match_flags: DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_CLASS ,
+
+#define MY_USB_DEVICE(vend,prod) \
+match_flags: USB_DEVICE_ID_MATCH_DEVICE , \
+idVendor: (vend), \
+idProduct: (prod),\
+bDeviceClass: (CDC_INTERFACE_CLASS),
+
+static __devinitdata struct usb_device_id id_table[] = {
+
+ // RNDIS devices Vend Prod bDeviceClass bInterfaceClass bInterfaceSubClass
+
+ {MY_USB_DEVICE(0x15ec, 0xf001)}, // Belcarra Network Demo
+ {MY_USB_DEVICE(0x12b9, 0xf001)}, // Belcarra Network Demo
+
+
+#if 1
+#if defined(CONFIG_USB_USBLAN_VENDORID) && defined(CONFIG_USB_USBLAN_PRODUCTID)
+ // A configured driver
+ {MY_USB_DEVICE(CONFIG_USB_USBLAN_VENDORID, CONFIG_USB_USBLAN_PRODUCTID)},
+#endif
+#endif
+
+ // extra null entry for module vendor_id/produc parameters and terminating entry
+ {}, {},
+};
+
+
+#ifdef MODULE
+MODULE_DEVICE_TABLE(usb, id_table);
+#endif
+
+#define ECHO_FCS
+#define ECHO_RCV
+
+#undef ECHO_TX_SKB
+#define ECHO_TX_URB
+
+__u32 crc32_table[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+#define CRC32_INITFCS 0xffffffff // Initial FCS value
+#define CRC32_GOODFCS 0xdebb20e3 // Good final FCS value
+
+#define CRC32_FCS(fcs, c) (((fcs) >> 8) ^ crc32_table[((fcs) ^ (c)) & 0xff])
+
+/* fcs_memcpy32 - memcpy and calculate fcs
+ * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm.
+ */
+static __u32 __inline__
+fcs_memcpy32(unsigned char *dp, unsigned char *sp, int len, __u32 fcs)
+{
+ for (; len-- > 0; fcs = CRC32_FCS(fcs, *dp++ = *sp++));
+ return fcs;
+}
+
+/* fcs_pad32 - pad and calculate fcs
+ * Pad and calculate fcs using ppp 32bit CRC algorithm.
+ */
+static __u32 __inline__
+fcs_pad32(unsigned char *dp, int len, __u32 fcs)
+{
+ for (; len-- > 0; fcs = CRC32_FCS(fcs, *dp++ = '\0'));
+ return fcs;
+}
+
+/* fcs_compute32 - memcpy and calculate fcs
+ * Perform a memcpy and calculate fcs using ppp 32bit CRC algorithm.
+ */
+static __u32 __inline__
+fcs_compute32(unsigned char *sp, int len, __u32 fcs)
+{
+ for (; len-- > 0; fcs = CRC32_FCS(fcs, *sp++));
+ return fcs;
+}
+
+void
+wait_for_sync(struct WORK_STRUCT *tq)
+{
+
+ // wait for pending bottom halfs to exit
+ while (PENDING_WORK_ITEM((*tq))){
+ SCHEDULE_TIMEOUT(1);
+ }
+
+ tq->data = 0;
+
+ while (PENDING_WORK_ITEM((*tq))) {
+ SCHEDULE_TIMEOUT(1);
+ }
+}
+
+
+#define RETRYTIME 2
+
+void
+skb_bad_crc(struct sk_buff *skb, __u32 fcs)
+{
+#ifdef ECHO_FCS
+ if (noisy_fcs) {
+ printk(KERN_INFO"%s: BAD FCS len: %4d crc: %08x last: %02x %02x %02x %02x\n", __FUNCTION__,
+ skb->len, fcs, skb->data[skb->len - 4],
+ skb->data[skb->len - 3], skb->data[skb->len - 2], skb->data[skb->len - 1]
+ );
+ }
+ if (echo_fcs) {
+ int i;
+ unsigned char *cp = skb->data;
+ printk(KERN_INFO "%s: FAILED skb: %p head: %p data: %p tail: %p len: %d", __FUNCTION__,
+ skb, skb->head, skb->data, skb->tail, skb->len);
+ for (i = 0; i < skb->len; i++) {
+ if ((i % 32) == 0) {
+ printk("\nrcv[%02x]: ", i);
+ }
+ printk("%02x ", cp[i]);
+ }
+ printk("\n");
+ }
+#endif
+}
+
+#if 0
+static void
+dump_skb(struct sk_buff *skb, char *msg)
+{
+ int i;
+ unsigned char *cp = skb->data;
+
+ printk(KERN_INFO "\n%s", msg);
+ for (i = 0; i < skb->len; i++) {
+ if (!(i % 32)) {
+ printk("\n[%02x] ", i);
+ }
+ printk("%02x ", cp[i]);
+ }
+ printk("\n");
+}
+#endif
+
+/* ********************************************************************************************* */
+#if defined(LONG_STRING_OF_ZEROES_HACK)
+
+/* fermat
+ *
+ * This is a hack designed to help some broken hardware that cannot successfully
+ * transmit long strings of zero's without causing the host port to signal a
+ * status change and drop the connection.
+ *
+ */
+
+typedef unsigned char BYTE;
+typedef struct fermat {
+ int length;
+ BYTE power[256];
+} FERMAT;
+
+STATIC void fermat_init(void);
+STATIC void fermat_encode(BYTE *data, int length);
+STATIC void fermat_decode(BYTE *data, int length);
+
+STATIC int fermat_setup(FERMAT *p, int seed){
+ int i = 0;
+ unsigned long x,y;
+ y = 1;
+ do{
+ x = y;
+ p->power[i] = ( x == 256 ? 0 : x);
+ y = ( seed * x ) % 257;
+ i += 1;
+ }while( y != 1);
+ p->length = i;
+ return i;
+}
+
+STATIC void fermat_xform(FERMAT *p, BYTE *data, int length){
+ BYTE *pw = p->power;
+ int i, j;
+ BYTE * q ;
+ for(i = 0, j=0, q = data; i < length; i++, j++, q++){
+ if(j>=p->length){
+ j = 0;
+ }
+ *q ^= pw[j];
+ }
+}
+
+static FERMAT default_fermat;
+static const int primitive_root = 5;
+STATIC void fermat_init(){
+ (void) fermat_setup(&default_fermat, primitive_root);
+}
+
+// Here are the public official versions.
+// Change the primitive_root above to another primitive root
+// if you need better scatter. Possible values are 3 and 7
+
+
+STATIC void fermat_encode(BYTE *data, int length){
+ fermat_xform(&default_fermat, data, length);
+}
+
+STATIC void fermat_decode(BYTE *data, int length){
+ fermat_xform(&default_fermat, data, length);
+}
+#endif
+
+
+/* ********************************************************************************************* */
+
+STATIC void defer_skb(struct private *priv, struct sk_buff *skb, struct skb_cb *cb,
+ skb_state_t state, struct sk_buff_head *list);
+STATIC int unlink_urbs(struct sk_buff_head *q);
+STATIC void urb_tx_complete(struct urb *urb);
+STATIC void urb_rx_complete(struct urb *urb);
+#if 0
+STATIC void urb_dead_complete(struct urb *urb);
+#endif
+
+
+/* Network Configuration *********************************************************************** */
+
+/* sock_ioctl - perform an ioctl call to inet device
+ */
+static int sock_ioctl(u32 cmd, struct ifreq *ifreq)
+{
+ int rc = 0;
+ mm_segment_t save_get_fs = get_fs();
+ //printk(KERN_INFO"%s: cmd: %x\n", __FUNCTION__, cmd);
+ set_fs(get_ds());
+ rc = devinet_ioctl(cmd, ifreq);
+ set_fs(save_get_fs);
+ return rc;
+}
+
+/* sock_addr - setup a socket address for specified interface
+ */
+static int sock_addr(char * ifname, u32 cmd, u32 s_addr)
+{
+ struct ifreq ifreq;
+ struct sockaddr_in *sin = (void *) &(ifreq.ifr_ifru.ifru_addr);
+
+ //printk(KERN_INFO"%s: ifname: %s addr: %x\n", __FUNCTION__, ifname, ntohl(s_addr));
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname);
+
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = s_addr;
+
+ return sock_ioctl(cmd, &ifreq);
+}
+
+
+/* sock_flags - set flags for specified interface
+ */
+static int sock_flags(char * ifname, u16 oflags, u16 sflags, u16 rflags)
+{
+ int rc = 0;
+ struct ifreq ifreq;
+
+ //printk(KERN_INFO"%s: ifname: %s oflags: %x s_flags: %x r_flags: %x\n", __FUNCTION__, ifname, oflags, sflags, rflags);
+
+ memset(&ifreq, 0, sizeof(ifreq));
+ strcpy(ifreq.ifr_ifrn.ifrn_name, ifname);
+
+ oflags |= sflags;
+ oflags &= ~rflags;
+ ifreq.ifr_flags = oflags;
+
+ //printk(KERN_INFO"%s: -> ifr_flags: %x \n", __FUNCTION__, ifreq.ifr_flags);
+
+ THROW_IF ((rc = sock_ioctl(SIOCSIFFLAGS, &ifreq)), error);
+
+ //printk(KERN_INFO"%s: <- ifr_flags: %x \n", __FUNCTION__, ifreq.ifr_flags);
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ifconfig: cannot get/set interface flags (%d)\n", __FUNCTION__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+/* network_attach - configure interface
+ *
+ * This will use socket calls to configure the interface to the supplied
+ * ip address and make it active.
+ */
+STATIC int network_attach(struct net_device *net, u32 host_ip, u32 mask, int attach)
+{
+ int err = 0;
+
+ //printk(KERN_INFO"%s: net: %p host_ip: %08x mask: %08x attach: %d\n", __FUNCTION__, net, host_ip, mask, attach);
+ if (attach) {
+ u16 oflags = net ? net->flags : 0;
+
+ /* setup host_ip address, netwask, and broadcast address */
+ if (host_ip) {
+ THROW_IF ((err = sock_addr(net->name, SIOCSIFADDR, htonl(host_ip))), error);
+ if (mask) {
+ THROW_IF ((err = sock_addr(net->name, SIOCSIFNETMASK, htonl(mask))), error);
+ THROW_IF ((err = sock_addr(net->name, SIOCSIFBRDADDR, htonl(host_ip | ~mask))), error);
+ }
+ /* bring the interface up */
+ THROW_IF ((err = sock_flags(net->name, oflags, IFF_UP, 0)), error);
+ }
+
+
+ }
+ else {
+ u16 oflags = net ? net->flags : 0;
+ /* bring the interface down */
+ THROW_IF ((err = sock_flags(net->name, oflags, 0, IFF_UP)), error);
+ }
+
+ CATCH(error) {
+ printk(KERN_INFO"%s: ifconfig: cannot configure interface (%d)\n", __FUNCTION__, err);
+ return err;
+ }
+ return 0;
+}
+
+
+/* ********************************************************************************************* */
+
+/* Network Support Functions - these are called by the network layer *************************** */
+
+/* net_get_stats - network device get stats function
+ * Retreive network device stats structure.
+ */
+STATIC struct net_device_stats *
+net_get_stats(struct net_device *net)
+{
+ struct private *priv = (struct private *) net->priv;
+ //printk(KERN_INFO "%s:\n", __FUNCTION__);
+ return &priv->stats;
+}
+
+/* net_set_mac_addr - network device set mac address function
+ */
+STATIC int
+net_set_mac_address(struct net_device *net, void *p)
+{
+ struct private *priv = (struct private *) net->priv;
+ struct sockaddr *addr = p;
+
+ //printk(KERN_INFO "%s:\n", __FUNCTION__);
+ if (netif_running(net)) {
+ return -EBUSY;
+ }
+ memcpy(net->dev_addr, addr->sa_data, net->addr_len);
+ priv->addr_set = 1;
+ return 0;
+}
+
+/* net_change_mtu - network device set config function
+ * Set MTU, if running we can only change it to something less
+ * than or equal to MTU when PVC opened.
+ */
+STATIC int
+net_change_mtu(struct net_device *net, int mtu)
+{
+ //printk(KERN_INFO "%s:\n", __FUNCTION__);
+ if ((mtu < sizeof(struct ethhdr)) || (mtu > (MAX_PACKET - 4))) {
+ return -EINVAL;
+ }
+ if (netif_running(net)) {
+ if (mtu > net->mtu) {
+ return -EBUSY;
+ }
+ }
+ net->mtu = mtu;
+ return 0;
+}
+
+/* net_open - called by network layer to open network interface
+ */
+STATIC int
+net_open(struct net_device *net)
+{
+ struct private *priv = (struct private *) net->priv;
+
+ //printk(KERN_INFO "%s: priv#%08x net#%08x\n",__FUNCTION__,(u32)(void*)priv,(u32)(void*)net);
+ mutex_lock(&priv->mutex);
+ //printk(KERN_INFO "%s: AAA usbdev#%08x dataIF=%d alt=%d\n",__FUNCTION__,(u32)(void*)priv->usbdev, priv->data_bInterfaceNumber, priv->data_bAlternateSetting);
+
+ // enable traffic
+ //printk(KERN_INFO"%s: setting data interface bInterfaceNumber: %d bAlternateSetting: %d\n", __FUNCTION__,
+ // priv->data_bInterfaceNumber, priv->data_bAlternateSetting);
+
+#if defined(LINUX24)
+ if (usb_set_interface( priv->usbdev, priv->data_bInterfaceNumber, priv->data_bAlternateSetting)) {
+ err("usb_set_interface() failed");
+ }
+#endif
+
+ // XXX find interface ip / netmask, if netmask is 255.255.255.252
+ // then send ip and ip+1 to device as suggested addresses
+
+
+
+
+ //printk(KERN_INFO "%s: BBB\n",__FUNCTION__);
+
+ // tell the network layer to enable transmit queue
+ netif_start_queue(net);
+ //printk(KERN_INFO "%s: BBB\n",__FUNCTION__);
+
+ // call the bottom half to schedule some receive urbs
+ tasklet_schedule(&priv->bh);
+ //printk(KERN_INFO "%s: CCC\n",__FUNCTION__);
+
+ mutex_unlock(&priv->mutex);
+ //printk(KERN_INFO "%s: DDD\n",__FUNCTION__);
+ return 0;
+}
+
+/* net_stop - called by network layer to stop network interface
+ */
+STATIC int
+net_stop(struct net_device *net)
+{
+ struct private *priv = (struct private *) net->priv;
+
+ DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
+ DECLARE_WAITQUEUE(wait, current);
+
+ //printk(KERN_INFO "%s:\n",__FUNCTION__);
+
+ mutex_lock(&priv->mutex);
+
+ // tell the network layer to disable the transmit queue
+ netif_stop_queue(net);
+
+
+ // disable (if possible) network traffic
+ if (priv->nodata_bInterfaceNumber >= 0) {
+
+ //printk(KERN_INFO"%s: setting nodata interface bInterfaceNumber: %d bAlternateSetting: %d\n", __FUNCTION__,
+ // priv->nodata_bInterfaceNumber, priv->nodata_bAlternateSetting);
+
+ if (usb_set_interface( priv->usbdev, priv->nodata_bInterfaceNumber, priv->nodata_bAlternateSetting)) {
+ err("usb_set_interface() failed");
+ }
+ }
+
+
+ // setup a wait queue - this also acts as a flag to prevent bottom half from allocating more urbs
+ add_wait_queue(&unlink_wakeup, &wait);
+ priv->wait = &unlink_wakeup;
+
+ // move the tx and rx urbs into the done queue
+ unlink_urbs(&priv->txq);
+ unlink_urbs(&priv->rxq);
+
+ // wait for done queue to empty
+ while (skb_queue_len(&priv->rxq) || skb_queue_len(&priv->txq) || skb_queue_len(&priv->done)) {
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(TIMEOUT_JIFFIES * 1000);
+ }
+ priv->wait = 0;
+
+ // cleanup
+ remove_wait_queue(&unlink_wakeup, &wait);
+
+ mutex_unlock(&priv->mutex);
+ return 0;
+}
+
+
+/* net_tx_timeout - called by network layer to cancel outstanding skbs
+ */
+STATIC void
+net_tx_timeout(struct net_device *net)
+{
+ //struct private *priv = (struct private *) net->priv;
+ //unsigned char *cp;
+ //struct urb *urb;
+
+ //unsigned char deadbeef[] = "DEADBEEF";
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //unlink_urbs(&priv->txq);
+ //tasklet_schedule(&priv->bh);
+
+ //if (priv->unlink_task.sync == 0) {
+ // schedule_task(&priv->unlink_task);
+ //}
+
+ return;
+#if 0
+ // Attempt to send a short usb packet to the device, this will ensure that
+ // any partially completed bulk transfer will be terminated.
+
+ if (!(cp = kmalloc(sizeof(deadbeef), GFP_KERNEL))) {
+ return;
+ }
+ if (!(urb = USB_ALLOC_URB(0,GFP_ATOMIC))) {
+ kfree(cp);
+ return;
+ }
+ memcpy(cp, deadbeef, sizeof(deadbeef));
+
+ FILL_BULK_URB(urb, priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->data_ep_out),
+ cp, sizeof(deadbeef), urb_dead_complete, NULL);
+
+ urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK | USB_NO_FSBR;
+
+ //usb_endpoint_running(priv->usbdev, usb_pipeendpoint(priv->data_ep_out), usb_pipeout(priv->data_ep_out));
+ //usb_settoggle(priv->usbdev, usb_pipeendpoint(priv->data_ep_out), usb_pipeout(priv->data_ep_out), 0);
+
+ if (usb_submit_urb(urb)) {
+ kfree(cp);
+ urb->transfer_buffer = NULL;
+ usb_free_urb(urb);
+ }
+#endif
+}
+
+/* net_hard_start_xmit - called by network layer to transmit skb
+ */
+STATIC int
+net_hard_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct private *priv = (struct private *) net->priv;
+ struct urb *urb;
+ struct skb_cb *cb;
+ struct sk_buff *skb2;
+ int flags = in_interrupt()? GFP_ATOMIC : GFP_KERNEL;
+ __u32 fcs;
+ int length;
+ int pad;
+
+ //printk(KERN_INFO "%s:\n",__FUNCTION__);
+
+ echo_tx = 0;
+ // debug
+ if (echo_tx) {
+ int i;
+ unsigned char *cp = skb->data;
+ printk(KERN_INFO"%s: skb: %p %d\n", __FUNCTION__, skb, skb->len);
+ for (i = 0; i < skb->len; i++) {
+ if ((i % 32) == 0) {
+ printk("\ntx[%3x]: ", i);
+ }
+ printk("%02x ", cp[i]);
+ }
+ printk("\n");
+ }
+
+ // allocate urb
+ THROW_IF (priv->wait || !(urb = USB_ALLOC_URB(0,flags)), free_skb_only);
+
+ // calculate length required
+ if (priv->bmDataCapabilities & (DATA_PADBEFORE | DATA_PADAFTER)) {
+ // we need to pad so that after appending the CRC we have a multiple of packetsize
+ length = priv->data_ep_out_size * (((skb->len + 4 + 1) / priv->data_ep_out_size) + 1);
+ }
+ else {
+ // require a minimum of one full packet
+ length = MAX(priv->data_ep_out_size, skb->len + 4 + 1);
+ }
+
+ // allocate a new skb, copy data to it computing FCS,
+ // the extra bytes are for the CRC and optional pad byte
+ THROW_IF (!(skb2 = alloc_skb(length + 4, flags)), free_urb_and_skb); // XXX +4 ?
+
+ if (priv->bmDataCapabilities & DATA_CRC) {
+
+ fcs = fcs_memcpy32(skb_put(skb2, skb->len), skb->data, skb->len, CRC32_INITFCS);
+
+ //dump_skb(skb, "skb");
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+
+ if (priv->bmDataCapabilities & DATA_PADBEFORE) {
+ if ((pad = (length - skb->len - 4)) > 0) {
+ // pad to required length less four (CRC), copy fcs and append pad byte if required
+ fcs = fcs_pad32(skb_put(skb, pad), pad, fcs);
+ }
+ }
+
+ fcs = ~fcs;
+ *skb_put(skb, 1) = fcs & 0xff;
+ *skb_put(skb, 1) = (fcs >> 8) & 0xff;
+ *skb_put(skb, 1) = (fcs >> 16) & 0xff;
+ *skb_put(skb, 1) = (fcs >> 24) & 0xff;
+
+ if (priv->bmDataCapabilities & DATA_PADAFTER) {
+ while ((skb->len % priv->bPad) || !(skb->len % priv->data_ep_out_size)) skb_put(skb, 1);
+ }
+
+ // append a byte if required, we overallocated by one to allow for this
+ else if (!(skb->len % priv->data_ep_out_size)) {
+ *skb_put(skb, 1) = 0;
+ }
+
+ //dump_skb(skb, "skb with CRC");
+
+ }
+ else {
+ memcpy(skb_put(skb2, skb->len), skb->data, skb->len);
+
+ //dump_skb(skb, "skb");
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+
+ if (!(skb->len % priv->data_ep_out_size)) {
+ *skb_put(skb, 1) = 0;
+ }
+ }
+
+ // hand urb off to usb layer
+
+ cb = (struct skb_cb *) skb->cb;
+ cb->urb = urb;
+ cb->priv = priv;
+ cb->state = tx_start;
+ cb->jiffies = jiffies;
+
+ // urb->context ends up with pointer to skb
+ FILL_BULK_URB(urb, priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->data_ep_out), skb->data, skb->len,
+ urb_tx_complete, skb);
+
+ urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK | USB_NO_FSBR;
+#if defined(LINUX24)
+ urb->timeout = 2;
+#endif /* XXX FIX ME */
+
+
+ // submit the urb and restart (or not) the network device queue
+ //printk(KERN_INFO"%s: skb: %p %d urb %p len: %d\n", __FUNCTION__, skb, skb->len, urb, urb->transfer_buffer_length);
+
+ if (USB_SUBMIT_URB(urb)) {
+ netif_start_queue(net);
+ priv->stats.tx_dropped++;
+ usb_free_urb(urb);
+ THROW(free_urb_and_skb);
+ }
+ skb_queue_tail(&priv->txq, skb);
+ if (priv->txq.qlen < TX_QLEN) {
+ netif_start_queue(net);
+ }
+ else {
+ net->trans_start = jiffies;
+ }
+ CATCH(free_skb_only) {
+ CATCH(free_urb_and_skb) {
+ usb_free_urb(urb);
+ }
+ dev_kfree_skb_any(skb);
+ return NET_XMIT_DROP;
+ }
+ return NET_XMIT_SUCCESS;
+}
+
+
+/* Receive Related ***************************************************************************** */
+
+/* rx_submit - queue an urb to receive data
+ */
+STATIC void
+rx_submit(struct private *priv, struct urb *urb, int gpf)
+{
+ struct sk_buff *skb;
+ struct skb_cb *cb;
+ unsigned long flags;
+ int size;
+
+ //printk(KERN_INFO "%s:\n",__FUNCTION__);
+
+ //usblan_test_kalloc("AAA");
+ size = ((priv->net.mtu + 14 + 4 + priv->data_ep_in_size) / priv->data_ep_in_size) * priv->data_ep_in_size;
+
+ //printk(KERN_INFO"%s: ep_in: %d ep_in_size: %d size: %d urb#%08x\n", __FUNCTION__,
+ // priv->data_ep_in, priv->data_ep_in_size, size, (u32)(void*)urb);
+
+ //TBR-32-bit if (!(skb = alloc_skb(size + 2, gpf))) {
+ if (!(skb = alloc_skb(size + 4, gpf))) {
+ //printk(KERN_INFO "%s: alloc_skb() failed.\n",__FUNCTION__);
+ dbg("no rx skb");
+ tasklet_schedule(&priv->bh);
+ THROW(free_urb_only);
+ return;
+ }
+ //TBR-32-bit skb_reserve(skb, 2);
+ skb_reserve(skb, 4);
+ //usblan_test_kalloc("BBB");
+
+ cb = (struct skb_cb *) skb->cb;
+ cb->priv = priv;
+ cb->urb = urb;
+ cb->state = rx_start;
+ //usblan_test_kalloc("CCC");
+
+ //printk(KERN_INFO"%s: AAA\n", __FUNCTION__);
+ // urb->context ends up with pointer to skb
+ FILL_BULK_URB(urb, priv->usbdev, usb_rcvbulkpipe(priv->usbdev, priv->data_ep_in), skb->data, size, urb_rx_complete, skb);
+ //usblan_test_kalloc("DDD");
+
+ urb->transfer_flags |= USB_QUEUE_BULK;
+
+ //printk(KERN_INFO"%s: BBB\n", __FUNCTION__);
+ spin_lock_irqsave(&priv->rxq.lock, flags);
+ //printk(KERN_INFO"%s: CCC\n", __FUNCTION__);
+ if (netif_running(&priv->net)) {
+
+ //printk(KERN_INFO"%s: DDD urb %p\n", __FUNCTION__, urb);
+ //usblan_test_kalloc("EEE");
+
+ //printk(KERN_INFO "%s: starting URB #%08x\n",__FUNCTION__,(u32)(void*)urb);
+
+ spin_unlock_irqrestore(&priv->rxq.lock, flags);
+ if (USB_SUBMIT_URB(urb)) {
+ //printk(KERN_INFO "%s: submit failed freeing URB: %p\n", __FUNCTION__, urb);
+ //usblan_test_kalloc("FFF");
+ tasklet_schedule(&priv->bh);
+ THROW(free_urb_and_skb);
+ }
+ else {
+ //printk(KERN_INFO"%s: FFF skb %p\n", __FUNCTION__, skb);
+ __skb_queue_tail(&priv->rxq, skb);
+ }
+ //printk(KERN_INFO"%s: GGG urb %p\n", __FUNCTION__, urb);
+ //usblan_test_kalloc("GGG");
+ }
+ else {
+ spin_unlock_irqrestore(&priv->rxq.lock, flags);
+ //printk(KERN_INFO "%s: network stopped, freeing urb: %p\n", __FUNCTION__, urb);
+ THROW(free_urb_and_skb);
+ }
+
+ CATCH(free_urb_only) {
+
+ CATCH(free_urb_and_skb) {
+ printk(KERN_INFO"%s: error freeing skb %p\n", __FUNCTION__, skb);
+ dev_kfree_skb_any(skb);
+ }
+ printk(KERN_INFO"%s: error freeing urb %p\n", __FUNCTION__, urb);
+ usb_free_urb(urb);
+ }
+}
+
+/* urb_rx_complete - called by usb core layer when urb has been received
+ */
+STATIC void
+urb_rx_complete(struct urb *urb)
+{
+ struct sk_buff *skb;
+ struct skb_cb *cb;
+ struct private *priv;
+
+ //printk(KERN_INFO "%s: urb %p len: %d status=%d\n", __FUNCTION__, urb, urb->actual_length,urb->status);
+
+ if (!(skb = (struct sk_buff *) urb->context)) {
+ printk(KERN_ERR "%s: skb NULL\n", __FUNCTION__);
+ }
+ if (!(cb = (struct skb_cb *) skb->cb)) {
+ printk(KERN_ERR "%s: cb NULL\n", __FUNCTION__);
+ }
+ if (!(priv = cb->priv)) {
+ printk(KERN_ERR "%s: priv NULL\n", __FUNCTION__);
+ }
+
+
+ switch (urb->status) {
+ case 0:
+ priv->timeouts = 0;
+
+ if ((MIN_PACKET < urb->actual_length) && (urb->actual_length < MAX_PACKET)) {
+ cb->urb = NULL;
+ skb_put(skb, urb->actual_length);
+ defer_skb(priv, skb, cb, rx_done, &priv->rxq);
+ if (netif_running(&priv->net)) {
+ rx_submit(priv, urb, GFP_ATOMIC);
+ }
+ break;
+ }
+
+ /* FALLTHROUGH */
+
+ case -EOVERFLOW:
+ priv->stats.rx_over_errors++;
+ case -EILSEQ:
+ case -ECONNABORTED:
+ case -ETIMEDOUT:
+ priv->timeouts++;
+ priv->stats.rx_dropped++;
+ //printk(KERN_INFO"%s: RX_CLEANUP urb->status: %d timeout: %d\n", __FUNCTION__, urb->status, priv->timeouts);
+ defer_skb(priv, skb, cb, rx_cleanup, &priv->rxq);
+
+ // XXX provisional, this will attempt to force a reset for a device that
+ // there have been multiple receive timeouts. This is a host
+ // that is no longer responding to IN with a NAK. Typically this is
+ // due to a device that has stopped operation without dropping the
+ // usb control resistor to tell us.
+
+ if (priv->timeouts > 20) {
+ priv->timeouts = 0;
+ //printk(KERN_INFO "%s: scheduling reset task\n", __FUNCTION__);
+
+#if 0
+ if (priv->reset_task.sync == 0) {
+ schedule_task(&priv->reset_task);
+#else
+ if(!PENDING_WORK_ITEM(priv->reset_task)){
+ SCHEDULE_WORK(priv->reset_task);
+#endif
+ }
+ }
+ break;
+ }
+}
+
+
+/* bh_rx_process - called by bottom half to process received skb
+ */
+STATIC inline void
+bh_rx_process(struct private *priv, struct sk_buff *skb)
+{
+ __u32 fcs;
+
+#if 0
+ if (skb->len > (priv->net.mtu + 16 + 4 + 1 + 100)) {
+ printk(KERN_INFO "%s: URB too large\n", __FUNCTION__);
+ priv->stats.rx_length_errors++;
+ THROW(crc_error);
+ dev_kfree_skb(skb);
+ priv->stats.rx_errors++;
+ return;
+ }
+#endif
+#if 0
+ if (priv->bmDataCapabilities & DATA_FERMAT) {
+ fermat_decode(skb->data, skb->len);
+ }
+#endif
+ //printk(KERN_INFO "%s:\n",__FUNCTION__);
+ if (priv->bmDataCapabilities & DATA_CRC) {
+
+ // check if we need to check for extra byte
+ if ((skb->len % priv->data_ep_in_size) == 1) {
+
+ // check fcs across length minus one bytes
+ if ((fcs = fcs_compute32(skb->data, skb->len - 1, CRC32_INITFCS)) == CRC32_GOODFCS) {
+ // success, trim extra byte and fall through
+ skb_trim(skb, skb->len - 1);
+
+ priv->sawCRC = 1;
+ }
+ // failed, check additional byte
+ else if ((fcs = fcs_compute32(skb->data + skb->len - 1, 1, fcs)) != CRC32_GOODFCS) {
+ // failed
+ printk(KERN_INFO "%s: CRC fail on extra byte\n", __FUNCTION__);
+ THROW_IF(priv->sawCRC, crc_error);
+ return;
+ }
+ // success fall through, possibly with corrected length
+ }
+ // normal check across full frame
+ else if ((fcs = fcs_compute32(skb->data, skb->len, CRC32_INITFCS)) != CRC32_GOODFCS) {
+ printk(KERN_INFO "%s: CRC fail len: %d\n", __FUNCTION__, skb->len);
+ THROW_IF(priv->sawCRC, crc_error);
+ return;
+ }
+
+ // trim fcs
+ skb_trim(skb, skb->len - 4);
+ }
+
+ // debug
+ echo_rx = 0;
+ if (echo_rx) {
+ int i;
+ unsigned char *cp = skb->data;
+
+ for (i = 0; i < skb->len; i++) {
+ if ((i % 32) == 0) {
+ printk("\nrx[%3x]: ", i);
+ }
+ printk("%02x ", cp[i]);
+ }
+ printk("\n");
+ }
+
+ // push the skb up
+ memset(skb->cb, 0, sizeof(struct skb_cb));
+ skb->dev = &priv->net;
+ skb->protocol = eth_type_trans(skb, &priv->net);
+ skb->pkt_type = PACKET_HOST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ priv->stats.rx_packets++;
+ priv->stats.rx_bytes += skb->len;
+ if (netif_rx(skb)) {
+ printk(KERN_INFO "%s: submitting skb failed\n", __FUNCTION__);
+ }
+
+ CATCH(crc_error) {
+ dev_kfree_skb_any(skb);
+ priv->stats.rx_errors++;
+ }
+}
+
+
+/* Transmit Related **************************************************************************** */
+
+/* This is a version of usb_clear_halt() that doesn't read the status from
+ * the device -- this is because some devices crash their internal firmware
+ * when the status is requested after a halt
+ */
+STATIC int
+local_clear_halt(struct usb_device *dev, int pipe)
+{
+ int result;
+ int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7);
+
+ if ((result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * 3)))
+ {
+ return result;
+ }
+
+ // reset the toggles and endpoint flags
+ // usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+ // usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
+
+ return 0;
+}
+
+/* ctrl_task - called as kernel task to send clear halt message
+ */
+STATIC void
+ctrl_task(void *data)
+{
+ struct private *priv = (struct private *) data;
+
+ local_clear_halt(priv->usbdev, usb_sndbulkpipe(priv->usbdev, priv->data_ep_out));
+ netif_wake_queue(&priv->net);
+}
+
+/* reset_task - called as kernel task to send reset the device
+ */
+STATIC void
+reset_task(void *data)
+{
+ struct private *priv = (struct private *) data;
+
+ if (usb_reset_device(priv->usbdev)) {
+ printk(KERN_INFO "%s: reset failed\n", __FUNCTION__);
+ }
+}
+
+/* urb_tx_complete - called by usb core layer when network skb urb has been transmitted
+ */
+STATIC void
+urb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb;
+
+ //printk(KERN_INFO"%s: urb: %p\n", __FUNCTION__, urb);
+
+ if (urb->status) {
+ printk(KERN_INFO "%s: urb: %p status: %d\n", __FUNCTION__, urb, urb->status);
+ }
+
+ urb->dev = 0;
+
+ if ((skb = urb->context)) {
+ struct skb_cb *cb;
+ struct private *priv;
+
+ if (!(cb = (struct skb_cb *) skb->cb)) {
+ printk(KERN_ERR "%s: cb NULL skb: %p\n", __FUNCTION__, skb);
+ }
+ if (!(priv = cb->priv)) {
+ printk(KERN_ERR "%s: priv NULL skb: %p cb: %p\n", __FUNCTION__, skb, cb);
+ }
+
+ if (!cb->urb) {
+ printk(KERN_ERR "%s: urb NULL skb: %p cb: %p\n", __FUNCTION__, skb, cb);
+ }
+
+ if (cb->urb != urb) {
+ printk(KERN_ERR "%s: urb not urb skb: %p cb: %p cb->urb: %p urb: %p\n", __FUNCTION__,
+ skb, cb, cb->urb, urb);
+ }
+
+ if (urb->status == USB_ST_STALL) {
+ printk(KERN_ERR "%s: USB_ST_STALL\n", __FUNCTION__);
+#if defined(LINUX24)
+ if (priv->ctrl_task.sync == 0) {
+ schedule_task(&priv->ctrl_task);
+ }
+#else
+ if (!PENDING_WORK_ITEM(priv->ctrl_task)){
+ SCHEDULE_WORK(priv->ctrl_task);
+ }
+#endif
+
+ }
+ defer_skb(priv, skb, cb, tx_done, &priv->txq);
+ }
+}
+
+#if 0
+/* urb_dead_complete - called by usb core layer when deadbeef urb has been transmitted
+ */
+STATIC void
+urb_dead_complete(struct urb *urb)
+{
+ urb->dev = 0;
+
+ if (urb->transfer_buffer) {
+ kfree(urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+ usb_free_urb(urb);
+}
+#endif
+
+
+/* Bottom Half ********************************************************************************* */
+
+/* defer_skb - put an skb on done list and schedule bottom half if necessary
+ */
+STATIC void
+defer_skb(struct private *priv, struct sk_buff *skb, struct skb_cb *cb,
+ skb_state_t state, struct sk_buff_head *list)
+{
+ unsigned long flags;
+
+ if (!cb) {
+ printk(KERN_ERR "%s: cb is NULL!\n", __FUNCTION__);
+ }
+
+ //if !(cb->urb) {
+ // printk(KERN_ERR"%s: urb is NULL!\n", __FUNCTION__);
+ //}
+
+ cb->state = state;
+
+ if (!skb) {
+ printk(KERN_ERR "%s: skb is NULL!\n", __FUNCTION__);
+ return;
+ }
+
+ spin_lock_irqsave(&list->lock, flags);
+ __skb_unlink(skb, list);
+ spin_unlock(&list->lock);
+
+
+ // link to done queue
+ spin_lock(&priv->done.lock);
+ __skb_queue_tail(&priv->done, skb);
+
+ if (priv->done.qlen == 1) {
+ tasklet_schedule(&priv->bh);
+ }
+ spin_unlock_irqrestore(&priv->done.lock, flags);
+}
+
+
+/* unlink_urbs - tell usb core layer that we want it to abandon attempts to send/receive urbs
+ */
+STATIC int
+unlink_urbs(struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+ int count = 0;
+
+ // move from the current queue to the unlink queue
+ while ((skb = skb_dequeue(q))) {
+ struct skb_cb *cb;
+ struct urb *urb;
+ struct private *priv;
+ int retval;
+
+ if (!(cb = (struct skb_cb *) skb->cb)) {
+ printk(KERN_ERR "%s: cb NULL\n", __FUNCTION__);
+ continue;
+ }
+
+ if (!(urb = cb->urb)) {
+ printk(KERN_ERR "%s: urb NULL\n", __FUNCTION__);
+ continue;
+ }
+
+ if (!(priv = cb->priv)) {
+ printk(KERN_ERR "%s: priv NULL\n", __FUNCTION__);
+ continue;
+ }
+
+ // place them here until they can be processed after unlinking
+ skb_queue_tail(&priv->unlink, skb);
+
+ //printk(KERN_INFO "%s: unlinking skb: %p len: %d jiffs: %ld\n", __FUNCTION__,
+ // skb, skb->len, jiffies - cb->jiffies);
+
+ // usb core layer will call rx_complete() with appropriate status so that we can remove
+ urb->transfer_flags |= USB_ASYNC_UNLINK;
+ if ((retval = usb_unlink_urb(urb)) < 0) {
+ dbg("unlink urb err, %d", retval);
+ }
+ else {
+ count++;
+ }
+ }
+ return count;
+}
+
+/* unlink_task - called as kernel task to send crc message
+ */
+STATIC void
+unlink_task(void *data)
+{
+ struct private *priv = (struct private *) data;
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ unlink_urbs(&priv->txq);
+ tasklet_schedule(&priv->bh);
+}
+
+
+/* bh - bottom half
+ */
+STATIC void
+bh(unsigned long data)
+{
+ struct private *priv = (struct private *) data;
+ struct sk_buff *skb;
+
+ //printk(KERN_INFO "%s: priv#%08x\n", __FUNCTION__, (u32)(void*)priv);
+
+ if (!priv) {
+ printk(KERN_INFO "%s: priv NULL\n", __FUNCTION__);
+ return;
+ }
+
+ // process all skb's on the done queue
+ while ((skb = skb_dequeue(&priv->done))) {
+
+ struct skb_cb *cb;
+
+ //printk(KERN_INFO "%s: skb#%08x\n", __FUNCTION__, (u32)(void*)skb);
+
+ if (!(cb = (struct skb_cb *) skb->cb)) {
+ printk(KERN_INFO "%s: cb NULL skb: %p\n", __FUNCTION__, skb);
+ continue;
+ }
+
+ switch (cb->state) {
+ case rx_done:
+ // printk(KERN_INFO"%s: rx_done skb: %p\n", __FUNCTION__, skb);
+ bh_rx_process(priv, skb);
+ break;
+
+ case tx_done:
+ // printk(KERN_INFO"%s: tx_done skb: %p\n", __FUNCTION__, skb);
+ {
+ struct urb *urb;
+ if (!(urb = cb->urb)) {
+ printk(KERN_INFO "%s: urb NULL skb: %p cb: %p\n", __FUNCTION__, skb, cb);
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ if (urb->status) {
+ priv->stats.tx_errors++;
+ //printk(KERN_INFO "%s: urb: %p skb: %p status: %d\n", __FUNCTION__,
+ // urb, skb, cb->urb->status);
+ urb->transfer_buffer_length = 0;
+ urb->transfer_buffer = NULL;
+ //usb_free_urb(urb);
+ //dev_kfree_skb_any(skb);
+ }
+ else {
+ priv->stats.tx_packets++;
+ priv->stats.tx_bytes += skb->len;
+ usb_free_urb(urb);
+ dev_kfree_skb_any(skb);
+ }
+ //usb_free_urb(urb);
+ //dev_kfree_skb_any(skb);
+ break;
+ }
+ case rx_cleanup:
+ //printk(KERN_INFO"%s: rx_cleanup skb: %p\n", __FUNCTION__, skb);
+ {
+ struct urb *urb;
+ if (!(urb = cb->urb)) {
+ printk(KERN_INFO "%s: urb NULL skb: %p cb: %p\n", __FUNCTION__, skb, cb);
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ if (urb) {
+ usb_free_urb(urb);
+ }
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ case unknown:
+ case tx_start:
+ case rx_start:
+ printk(KERN_INFO "%s: UNKNOWN\n", __FUNCTION__);
+ printk(KERN_INFO "%s: inconsistant cb state: %d\n", __FUNCTION__, cb->state);
+ break;
+ }
+ }
+
+ //printk(KERN_INFO "%s: priv->wait#%08x\n", __FUNCTION__, (u32)(void*)priv->wait);
+
+ // are we waiting for pending urbs to complete?
+ if (priv->wait) {
+ if (!(priv->txq.qlen + priv->rxq.qlen + priv->done.qlen + priv->unlink.qlen)) {
+ //printk(KERN_INFO "%s: wakeup\n", __FUNCTION__);
+ wake_up(priv->wait);
+ }
+ }
+
+ // do we need to queue up receive urbs?
+ else if (netif_running(&priv->net)) {
+
+ while ((priv->rxq.qlen < RX_QLEN) && (priv->timeouts < 4)) {
+ struct urb *urb;
+
+ //usblan_test_kalloc("bhR0");
+ //printk(KERN_INFO "%s: allocating receive urb ATOMIC#%08x KERNEL#%08x\n", __FUNCTION__,
+ // GFP_ATOMIC,GFP_KERNEL);
+ // allocate an urb and use rx_submit to prepare and add it to the rxq
+ if ((urb = USB_ALLOC_URB(0,GFP_ATOMIC))) {
+ //usblan_test_kalloc("bhR1");
+ //printk(KERN_INFO "%s: allocated receive urb#%08x\n", __FUNCTION__,(u32)(void*)urb);
+ rx_submit(priv, urb, GFP_ATOMIC);
+ //usblan_test_kalloc("bhR2");
+ }
+ else {
+ // we failed, schedule another run to try again
+ //printk(KERN_INFO "%s: allocating receive urb failed\n", __FUNCTION__);
+ tasklet_schedule(&priv->bh);
+ }
+ }
+
+ if (priv->txq.qlen < TX_QLEN) {
+ //printk(KERN_INFO"%s: wake queue\n", __FUNCTION__);
+ netif_wake_queue(&priv->net);
+ }
+ }
+}
+
+
+
+
+/* USB Functions - Probe and Disconnect ******************************************************** */
+
+#define BELCARRA_SETTIME 0x04
+#define BELCARRA_SETIP 0x05
+#define BELCARRA_SETMSK 0x06
+#define BELCARRA_SETROUTER 0x07
+
+
+static void vendor_callback(struct urb *urb)
+{
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ //wake_up(priv->ctrl_wait);
+
+ usb_free_urb(urb);
+ //printk(KERN_INFO"%s: finish\n", __FUNCTION__);
+}
+
+
+int VendorOut(struct private *priv, u8 bRequest, u16 wValue, u16 wIndex)
+{
+ struct usb_ctrlrequest *ctrl_request = &priv->ctrl_request;
+ int rc = 0;
+
+ //DECLARE_WAIT_QUEUE_HEAD(vendor_wakeup);
+ //DECLARE_WAITQUEUE(wait, current);
+
+ //printk(KERN_INFO"%s: bRequest: %02x wValue: %04x wIndex: %04x\n", __FUNCTION__, bRequest, wValue, wIndex);
+
+ // setup a wait queue - this also acts as a flag to prevent bottom half from allocating more urbs
+ //add_wait_queue(&vendor_wakeup, &wait);
+ //priv->ctrl_wait = &vendor_wakeup;
+
+ mutex_lock(&priv->mutex);
+
+ THROW_UNLESS((priv->ctrl_urb = USB_ALLOC_URB(0, GFP_ATOMIC)), error);
+
+ // create urb
+ ctrl_request->bRequestType = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ ctrl_request->bRequest = bRequest;
+ ctrl_request->wValue = wValue;
+ ctrl_request->wIndex = wIndex;
+ ctrl_request->wLength = 0;
+
+ FILL_CONTROL_URB(priv->ctrl_urb, priv->usbdev,
+ usb_sndctrlpipe(priv->usbdev, 0),
+ (char *)&priv->ctrl_request,
+ NULL, 0, vendor_callback, priv
+ );
+
+ // submit urb
+
+ THROW_IF(USB_SUBMIT_URB(priv->ctrl_urb), error);
+
+ // sleep
+ //current->state = TASK_UNINTERRUPTIBLE;
+ //schedule_timeout(TIMEOUT_JIFFIES * 1000);
+ //priv->ctrl_wait = NULL;
+
+
+ CATCH(error) {
+ rc = -EINVAL;
+ }
+
+ //if (priv->ctrl_urb)
+ // usb_free_urb(priv->ctrl_urb);
+ //priv->ctrl_urb = NULL;
+
+ // cleanup
+ //remove_wait_queue(&vendor_wakeup, &wait);
+ mutex_unlock(&priv->mutex);
+ return rc;
+}
+
+int VendorOutLong(struct private *priv, u16 bRequest, u32 data)
+{
+ //printk(KERN_INFO"%s: bRequest: %02x data: %04x\n", __FUNCTION__, bRequest, data);
+ data = ntohl(data);
+ return VendorOut(priv, bRequest, (u16)(data>>16), (u16)(data&0xffff));
+}
+
+void network_notify(struct private *priv, u32 host_ip, u32 mask, u32 client_ip)
+{
+
+
+ //printk(KERN_INFO"%s: client_ip: %08x mask: %08x host_ip: %x\n", __FUNCTION__, client_ip, mask, host_ip);
+ THROW_IF(VendorOutLong(priv, BELCARRA_SETIP, client_ip), error );
+ THROW_IF(VendorOutLong(priv, BELCARRA_SETMSK, mask), error );
+ THROW_IF(VendorOutLong(priv, BELCARRA_SETROUTER, host_ip), error );
+
+ //printk(KERN_INFO"%s: host_ip\n", __FUNCTION__);
+ //printk(KERN_INFO"%s: finished\n", __FUNCTION__);
+
+ CATCH(error) {
+ }
+}
+
+
+#if 0
+/* This is a version of usb_clear_halt() that doesn't read the status from
+ * the device -- this is because some devices crash their internal firmware
+ * when the status is requested after a halt
+ */
+STATIC int
+set_crc(struct usb_device *dev, int pipe)
+{
+ return usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ 0x3,
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0,
+ usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7),
+ NULL, 0, HZ * 3);
+}
+#endif
+
+#if 0
+/* crc_task - called as kernel task to send crc message
+ */
+STATIC void
+crc_task(void *data)
+{
+ struct private *priv = (struct private *) data;
+ set_crc(priv->usbdev, usb_sndctrlpipe(priv->usbdev, 0));
+}
+#endif
+
+
+/* create_private - create private data structure and initialize network interface
+ */
+STATIC struct private *
+create_private(int devnum)
+{
+ struct private *priv;
+ struct net_device *net;
+
+ if (!(priv = kmalloc(sizeof(struct private), GFP_KERNEL))) {
+ return NULL;
+ }
+
+ memset(priv, 0, sizeof(struct private));
+
+ net = &priv->net;
+#ifdef MODULE
+ SET_MODULE_OWNER(net);
+#endif
+ net->priv = priv;
+ //printk(KERN_INFO "%s: priv#%08x net#%08x\n",__FUNCTION__,(u32)(void*)priv,(u32)(void*)net);
+ // XXX usbb vs usbl
+
+ switch(priv->usbdnet_device_type) {
+
+ case usbdnet_blan:
+ strcpy(net->name, "usbl%d");
+ break;
+ default:
+ strcpy(net->name, "usb%d");
+ break;
+ }
+ memcpy(priv->dev_addr, default_addr, ETH_ALEN);
+
+ priv->dev_addr[ETH_ALEN - 1] = (unsigned char) devnum;
+
+ memcpy(net->dev_addr, default_addr, sizeof(default_addr));
+ net->dev_addr[ETH_ALEN - 1] = devnum;
+
+ ether_setup(net);
+
+ net->set_mac_address = net_set_mac_address;
+ net->hard_start_xmit = net_hard_start_xmit;
+ net->get_stats = net_get_stats;
+ net->change_mtu = net_change_mtu;
+ net->open = net_open;
+ net->stop = net_stop;
+ net->tx_timeout = net_tx_timeout;
+ net->watchdog_timeo = TIMEOUT_JIFFIES;
+
+ //printk(KERN_INFO"%s: finis\n", __FUNCTION__);
+ return priv;
+}
+
+STATIC struct usb_device_id *
+idp_search(int idVendor, int idProduct)
+{
+ struct usb_device_id *idp;
+
+ //printk(KERN_INFO "%s: look for idVendor: %04x idProduct: %04x\n", __FUNCTION__, idVendor, idProduct);
+
+ // search id_table for a match
+ for (idp = id_table;; idp++) {
+
+ //printk(KERN_INFO "%s: looking at idVendor: %04x idProduct: %04x\n", __FUNCTION__,
+ // idp->idVendor, idp->idProduct);
+
+ // end of table
+ BREAK_IF (!idp->idVendor && !idp->idProduct);
+
+ // check for match
+ if ((idp->idVendor == idVendor) && (idp->idProduct == idProduct)) {
+ //printk(KERN_INFO "%s: MATCH\n", __FUNCTION__);
+ return idp;
+ }
+ }
+ return NULL;
+}
+
+
+
+/*
+ * See if we have a CDC style communications interface:
+ *
+ * 1. Must have specified class, subclass and protocol
+ * 2. May have an optional INTERRUPT endpoint.
+ *
+ */
+STATIC int find_interface_comm(
+ struct private *priv,
+ struct usb_interface *comm_interface,
+ int class, int subclass, int protocol)
+{
+ int alternate_number;
+ struct usb_interface_descriptor *interface_descriptor;
+
+ //printk(KERN_INFO"%s: class: %x subclass: %x protocol: %x\n", __FUNCTION__, class, subclass, protocol );
+
+ // iterate across interface alternate descriptors looking for suitable one
+ for (alternate_number = 0; alternate_number < comm_interface->num_altsetting; alternate_number++) {
+
+// interface_descriptor = comm_interface->altsetting + alternate_number;
+ interface_descriptor = USB_ALTSETTING(comm_interface,alternate_number);
+ priv->comm_ep_in = 0;
+
+ //printk(KERN_INFO"%s: alt: %x class: %x subclass: %x protocol: %x\n", __FUNCTION__,
+ // alternate_number,
+ // interface_descriptor->bInterfaceClass,
+ // interface_descriptor->bInterfaceSubClass,
+ // interface_descriptor->bInterfaceProtocol);
+
+ // check for class, sub-class and too many endpoints (zero or one ok)
+ CONTINUE_IF ((interface_descriptor->bInterfaceClass != class) ||
+ (interface_descriptor->bInterfaceSubClass != subclass) ||
+ (interface_descriptor->bInterfaceProtocol != protocol) ||
+ (1 < interface_descriptor->bNumEndpoints) );
+
+ // if we have an endpoint check for validity
+ if (interface_descriptor->bNumEndpoints) {
+ //struct usb_endpoint_descriptor *endpoint = interface_descriptor->endpoint;
+ struct usb_endpoint_descriptor *endpoint = USB_IFC2EP(interface_descriptor,0);
+ CONTINUE_IF (!(endpoint->bEndpointAddress & USB_DIR_IN) ||
+ (endpoint->bmAttributes != USB_ENDPOINT_XFER_INT));
+ priv->comm_ep_in = endpoint->bEndpointAddress & 0x7f;
+ priv->comm_ep_in_size = endpoint->wMaxPacketSize;
+ //printk(KERN_INFO"%s: ep found %d\n", __FUNCTION__, priv->comm_ep_in);
+ }
+
+ priv->comm_bInterfaceNumber = interface_descriptor->bInterfaceNumber;
+ priv->comm_bAlternateSetting = interface_descriptor->bAlternateSetting;
+
+ //printk(KERN_INFO"%s: found %d %d\n", __FUNCTION__,
+ // priv->comm_bInterfaceNumber, priv->comm_bAlternateSetting);
+ return 0;
+ }
+ return -1;
+}
+
+
+/*
+ * See if we can have a CDC style data interface:
+ *
+ * 1. Must have specified class, subclass and protocol
+ * 2. Must have (only) a BULK-IN and BULK-OUT endpoint.
+ * 3. Optionally May have INTERRUPT endpoint.
+ *
+ */
+STATIC int find_interface_data(
+ struct private *priv,
+ struct usb_interface *data_interface,
+ int class, int subclass, int protocol,
+ int allow_interrupt)
+{
+ int alternate_number;
+ struct usb_interface_descriptor *interface_descriptor;
+
+ //printk(KERN_INFO"%s: class: %x subclass: %x protocol: %x\n", __FUNCTION__, class, subclass, protocol );
+
+ for (alternate_number = 0; alternate_number < data_interface->num_altsetting; alternate_number++) {
+
+ //interface_descriptor = data_interface->altsetting + alternate_number;
+ interface_descriptor = USB_ALTSETTING(data_interface,alternate_number);
+ priv->data_ep_in = priv->data_ep_out = 0;
+
+ //printk(KERN_INFO"%s: alt: %d class: %x subclass: %x protocol: %x endpoints: %d\n", __FUNCTION__,
+ // alternate_number,
+ // interface_descriptor->bInterfaceClass,
+ // interface_descriptor->bInterfaceSubClass,
+ // interface_descriptor->bInterfaceProtocol,
+ // interface_descriptor->bNumEndpoints);
+
+ // check for class, sub-class and wrong number of endpoints (must be two)
+ CONTINUE_IF ((interface_descriptor->bInterfaceClass != class) ||
+ (interface_descriptor->bInterfaceSubClass != subclass) ||
+ (interface_descriptor->bInterfaceProtocol != protocol) );
+
+ CONTINUE_IF (allow_interrupt && !((1 < interface_descriptor->bNumEndpoints)
+ && (3 >= interface_descriptor->bNumEndpoints)));
+
+ CONTINUE_IF (!allow_interrupt && (2 != interface_descriptor->bNumEndpoints));
+
+ // check endpoints for validity, must be BULK and we want one in each direction, no more, no less
+ if (interface_descriptor->bNumEndpoints) {
+ int endpoint_number;
+ for (endpoint_number = 0; endpoint_number < interface_descriptor->bNumEndpoints; endpoint_number++) {
+ //struct usb_endpoint_descriptor *endpoint = interface_descriptor->endpoint + endpoint_number;
+ struct usb_endpoint_descriptor *endpoint = USB_IFC2EP(interface_descriptor,endpoint_number);
+
+ BREAK_IF (USB_ENDPOINT_XFER_BULK != endpoint->bmAttributes);
+
+ if (endpoint->bEndpointAddress & USB_DIR_IN) {
+ priv->data_ep_in = endpoint->bEndpointAddress & 0x7f;
+ priv->data_ep_in_size = endpoint->wMaxPacketSize;
+ }
+ else {
+ priv->data_ep_out = endpoint->bEndpointAddress & 0x7f;
+ priv->data_ep_out_size = endpoint->wMaxPacketSize;
+ }
+ }
+ }
+
+ CONTINUE_IF (!priv->data_ep_in || !priv->data_ep_out);
+
+ //printk(KERN_INFO"%s: ep found %d %d\n", __FUNCTION__, priv->data_ep_in, priv->data_ep_out);
+
+ priv->data_bInterfaceNumber = interface_descriptor->bInterfaceNumber;
+ priv->data_bAlternateSetting = interface_descriptor->bAlternateSetting;
+
+ //printk(KERN_INFO"%s: found %d %d\n", __FUNCTION__,
+ // priv->data_bInterfaceNumber, priv->data_bAlternateSetting);
+ return 0;
+ }
+ //printk(KERN_INFO"%s: not found\n", __FUNCTION__);
+ return -1;
+}
+
+/*
+ * See if we can have a CDC style no-data interface:
+ *
+ * 1. Must have specified class, subclass and protocol
+ * 2. Must have a zero endpoints.
+ */
+STATIC int find_interface_nodata(
+ struct private *priv,
+ struct usb_interface *data_interface,
+ int class, int subclass, int protocol)
+{
+ int alternate_number;
+ struct usb_interface_descriptor *interface_descriptor;
+
+ for (alternate_number = 0; alternate_number < data_interface->num_altsetting; alternate_number++) {
+
+ //interface_descriptor = data_interface->altsetting + alternate_number;
+ interface_descriptor = USB_ALTSETTING(data_interface,alternate_number);
+
+ //printk(KERN_INFO"%s: alt: %d class: %d subclass: %d endpoints: %d\n", __FUNCTION__, alternate_number,
+ // interface_descriptor->bInterfaceClass,
+ // interface_descriptor->bInterfaceSubClass,
+ // interface_descriptor->bNumEndpoints);
+
+ // check for class, sub-class, protocol and verify no endpoints
+ CONTINUE_IF ((interface_descriptor->bInterfaceClass != class) ||
+ (interface_descriptor->bInterfaceSubClass != subclass) ||
+ (interface_descriptor->bInterfaceProtocol != protocol) ||
+ (interface_descriptor->bNumEndpoints));
+
+ priv->nodata_bInterfaceNumber = interface_descriptor->bInterfaceNumber;
+ priv->nodata_bAlternateSetting = interface_descriptor->bAlternateSetting;
+
+ //printk(KERN_INFO"%s: found %d %d\n", __FUNCTION__,
+ // priv->nodata_bInterfaceNumber, priv->nodata_bAlternateSetting);
+
+ return 0;
+ }
+ //printk(KERN_INFO"%s: not found\n", __FUNCTION__);
+ return -1;
+}
+
+/*
+ * See if we can have a MDLM style no-data interface:
+ *
+ * 1. Must have specified class, subclass and protocol
+ * 2. Must have have BULK-IN and BULK-OUT endpoints
+ * 3. May have INTERRUPT endpoint
+ *
+ */
+STATIC int find_interface_mdlm(
+ struct private *priv,
+ struct usb_interface *mdlm_interface,
+ int class,
+ int subclass,
+ int protocol,
+ char *guid
+ )
+{
+ struct usb_interface_descriptor *interface_descriptor;
+ struct usb_class_descriptor *extra;
+ int extralen;
+
+ if (mdlm_interface->num_altsetting > 1) {
+ printk(KERN_INFO"%s: too many intefaces num_altsetting: %d\n", __FUNCTION__, mdlm_interface->num_altsetting);
+ return -1;
+ }
+
+ //interface_descriptor = mdlm_interface->altsetting;
+ interface_descriptor = USB_ALTSETTING(mdlm_interface,0);
+
+ //printk(KERN_INFO"%s: class: %d subclass: %d endpoints: %d\n", __FUNCTION__,
+ // interface_descriptor->bInterfaceClass,
+ // interface_descriptor->bInterfaceSubClass,
+ // interface_descriptor->bNumEndpoints
+ // );
+
+ // check for class, sub-class, protocol and correct number of endpoints (two or three ok)
+ if ((interface_descriptor->bInterfaceClass != class) ||
+ (interface_descriptor->bInterfaceSubClass != subclass) ||
+ (interface_descriptor->bInterfaceProtocol != protocol) ||
+ !((1 < interface_descriptor->bNumEndpoints) && (3 >= interface_descriptor->bNumEndpoints))
+ )
+ {
+ return -1;
+ }
+
+ if (interface_descriptor->bNumEndpoints) {
+ int endpoint_number;
+ priv->comm_ep_in = priv->data_ep_in = priv->data_ep_out = 0;
+ for (endpoint_number = 0; endpoint_number < interface_descriptor->bNumEndpoints; endpoint_number++) {
+ //struct usb_endpoint_descriptor *endpoint = interface_descriptor->endpoint + endpoint_number;
+ struct usb_endpoint_descriptor *endpoint = USB_IFC2EP(interface_descriptor,endpoint_number);
+
+ if (USB_ENDPOINT_XFER_BULK == endpoint->bmAttributes) {
+ if (endpoint->bEndpointAddress & USB_DIR_IN) {
+ priv->data_ep_in = endpoint->bEndpointAddress & 0x7f;
+ priv->data_ep_in_size = endpoint->wMaxPacketSize;
+ }
+ else {
+ priv->data_ep_out = endpoint->bEndpointAddress & 0x7f;
+ priv->data_ep_out_size = endpoint->wMaxPacketSize;
+ }
+ }
+ else if ((USB_ENDPOINT_XFER_INT == endpoint->bmAttributes) &&
+ (endpoint->bEndpointAddress & USB_DIR_IN))
+ {
+ priv->comm_ep_in = endpoint->bEndpointAddress & 0x7f;
+ priv->comm_ep_in_size = endpoint->wMaxPacketSize;
+ }
+ else {
+ return -1;
+ }
+ }
+ }
+
+ if (!priv->data_ep_in || !priv->data_ep_out) {
+ return -1;
+ }
+
+ extra = (struct usb_class_descriptor *)USB_IFC2HOST(interface_descriptor)->extra;
+ extralen = USB_IFC2HOST(interface_descriptor)->extralen;
+ while (extra && (extralen > 0)) {
+
+ u8 *cp = (u8 *) extra;
+
+ //printk(KERN_INFO"%s: %p extralen: %02x bLength: %02x bDescriptorType: %02x bDescriptorSubType: %02x\n",
+ // __FUNCTION__, extra, extralen,
+ // extra->bLength, extra->bDescriptorType, extra->bDescriptorSubType);
+
+ BREAK_IF(!extra->bLength);
+
+ // check for CS descriptors (0x24)
+ switch(extra->bDescriptorType) {
+
+
+ case USB_DT_CS_INTERFACE:
+
+ //printk(KERN_INFO"%s: CS bDescriptorSubType: %02x\n", __FUNCTION__, extra->bDescriptorSubType);
+
+ // then check for MDLM Functional or Detail descriptors
+ switch (extra->bDescriptorSubType) {
+
+ // check for bGuid match
+ case MDLM_FUNCTIONAL:
+ {
+ struct usb_mdlm_functional_descriptor *functional =
+ (struct usb_mdlm_functional_descriptor *) extra;
+ //u8 *bGuid = (u8 *)&functional->bGuid;
+ //printk(KERN_INFO"%s: FUNCTIONAL bGUID %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ // __FUNCTION__,
+ // bGuid[0],bGuid[1],bGuid[2],bGuid[3],
+ // bGuid[4],bGuid[5],bGuid[6],bGuid[7]
+ // );
+
+ //printk(KERN_INFO"%s: FUNCTIONAL bGUID %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ // __FUNCTION__,
+ // bGuid[8],bGuid[9],bGuid[10],bGuid[11],
+ // bGuid[12],bGuid[13],bGuid[14],bGuid[15]
+ // );
+
+ RETURN_IF( -1, memcmp(guid, functional->bGuid, sizeof(functional->bGuid) != 0 ));
+ break;
+ }
+
+ // find details
+ case MDLM_DETAIL:
+ {
+ struct usb_mdlm_detail_descriptor *mdlm = (struct usb_mdlm_detail_descriptor *) extra;
+
+ //printk(KERN_INFO"%s: DETAIL bGuidDescriptorType\n", __FUNCTION__);
+
+ // figure out what type of detail record it is
+ switch (mdlm->bGuidDescriptorType) {
+ case MDLM_SAFE_GUID:
+ {
+ struct usb_safe_detail_descriptor *safe =
+ (struct usb_safe_detail_descriptor *) extra;
+ //printk(KERN_INFO"%s: SAFE bmDataCapabilities: %02x\n",
+ // __FUNCTION__, safe->bmDataCapabilities);
+ priv->bmNetworkCapabilities = safe->bmNetworkCapabilities;
+ priv->bmDataCapabilities = safe->bmDataCapabilities;
+ break;
+ }
+ case MDLM_BLAN_GUID:
+ {
+ struct usb_blan_detail_descriptor *blan =
+ (struct usb_blan_detail_descriptor *) extra;
+
+ //printk(KERN_INFO"%s: BLAN bmDataCapabilities: %02x\n",
+ // __FUNCTION__, blan->bmDataCapabilities);
+ priv->bmNetworkCapabilities = blan->bmNetworkCapabilities;
+ priv->bmDataCapabilities = blan->bmDataCapabilities;
+ priv->bPad = blan->bPad;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ extralen -= extra->bLength;
+ cp = cp + extra->bLength;
+ extra = (struct usb_class_descriptor *) cp;
+ }
+
+ priv->data_bInterfaceNumber = interface_descriptor->bInterfaceNumber;
+ priv->data_bAlternateSetting = interface_descriptor->bAlternateSetting;
+
+ //printk(KERN_INFO"%s: found %d %d\n", __FUNCTION__, priv->data_bInterfaceNumber, priv->data_bAlternateSetting);
+ return 0;
+}
+
+/*
+ * See if we can have a CDC interface:
+ *
+ * 1. class CDC_INTERFACE_CLASS
+ * 2. subclass CDC_INTERFACE_SUBCLASS
+ * 3. protocol DEFAULT_PROTOCOL
+ *
+ */
+STATIC int verify_ethernet_comm_interface(
+ struct usb_device *device,
+ struct private *priv,
+ int comm_number, struct usb_interface *comm_interface,
+ int data_number, struct usb_interface *data_interface
+ )
+{
+ //printk(KERN_INFO"verify_ethernet_comm_interface:\n");
+
+ if (find_interface_comm(priv, comm_interface, CDC_INTERFACE_CLASS, CDC_INTERFACE_SUBCLASS, DEFAULT_PROTOCOL)) {
+ //printk(KERN_INFO"verify_ethernet_comm_interface: no comm\n");
+ return -1;
+ }
+
+ if (find_interface_data(priv, data_interface, DATA_INTERFACE_CLASS, 0, DEFAULT_PROTOCOL, 0)) {
+ //printk(KERN_INFO"verify_ethernet_comm_interface: no data\n");
+ return -1;
+ }
+
+ if (find_interface_nodata(priv, data_interface, DATA_INTERFACE_CLASS, 0, DEFAULT_PROTOCOL)) {
+ //printk(KERN_INFO"verify_ethernet_comm_interface: no nodata\n");
+ }
+
+ //printk(KERN_INFO"verify_ethernet_comm_interface: found\n");
+
+ priv->comm_interface = comm_number;
+ priv->data_interface = data_number;
+ priv->usbdnet_device_type = usbdnet_cdc;
+
+ return 0;
+}
+
+/*
+ * See if we can have an RNDIS interface:
+ *
+ * 1. class CDC_INTERFACE_CLASS
+ * 2. subclass RNDIS_INTERFACE_SUBCLASS
+ * 3. protocol VENDOR_SPECIFIC_PROTOCOL
+ *
+ */
+STATIC int verify_rndis_comm_interface(
+ struct usb_device *device,
+ struct private *priv,
+ int comm_number, struct usb_interface *comm_interface,
+ int data_number, struct usb_interface *data_interface
+ )
+{
+
+ //printk(KERN_INFO"verify_rndis_comm_interface:\n");
+
+ if (find_interface_comm(priv, comm_interface, CDC_INTERFACE_CLASS, RNDIS_INTERFACE_SUBCLASS, VENDOR_SPECIFIC_PROTOCOL)) {
+ //printk(KERN_INFO"verify_rndis_comm_interface: no comm\n");
+ return -1;
+ }
+
+ if (find_interface_data(priv, data_interface, DATA_INTERFACE_CLASS, 0, DEFAULT_PROTOCOL, 0)) {
+ //printk(KERN_INFO"verify_rndis_comm_interface: no data\n");
+ return -1;
+ }
+
+ //if (find_interface_nodata(priv, data_interface, DATA_INTERFACE_CLASS, 0, DEFAULT_PROTOCOL)) {
+ // printk(KERN_INFO"verify_rndis_comm_interface: no nodata\n");
+ //}
+
+ //printk(KERN_INFO"verify_rndis_comm_interface: found\n");
+
+ priv->comm_interface = comm_number;
+ priv->data_interface = data_number;
+ priv->usbdnet_device_type = usbdnet_rndis;
+
+ return 0;
+}
+
+
+/*
+ * See if we can have an MDLM-BLAN interface:
+ *
+ * 1. class CDC_INTERFACE_CLASS
+ * 2. subclass MDLM_INTERFACE_SUBCLASS
+ * 3. protocol DEFAULT_PROTOCOL
+ *
+ */
+STATIC int verify_blan_interface(
+ struct usb_device *device,
+ struct private *priv,
+ struct usb_interface *data_interface
+ )
+{
+ if (find_interface_mdlm(priv, data_interface,
+ CDC_INTERFACE_CLASS,
+ MDLM_INTERFACE_SUBCLASS,
+ DEFAULT_PROTOCOL,
+ BLAN_GUID))
+ {
+ printk(KERN_INFO"%s: not found\n", __FUNCTION__);
+ return -1;
+ }
+
+ //printk(KERN_INFO"%s: found bmDataCapabilities: %02x bPad %02x\n",
+ // __FUNCTION__, priv->bmDataCapabilities, priv->bPad);
+
+ priv->comm_interface = priv->data_interface = 0;
+ priv->usbdnet_device_type = usbdnet_blan;
+ return 0;
+}
+
+/*
+ * See if we can have an MDLM-SAFE interface:
+ *
+ * 1. class CDC_INTERFACE_CLASS
+ * 2. subclass MDLM_INTERFACE_SUBCLASS
+ * 3. protocol DEFAULT_PROTOCOL
+ *
+ */
+STATIC int verify_safe_interface(
+ struct usb_device *device,
+ struct private *priv,
+ struct usb_interface *data_interface
+ )
+{
+ if (find_interface_mdlm(priv, data_interface,
+ CDC_INTERFACE_CLASS,
+ MDLM_INTERFACE_SUBCLASS,
+ DEFAULT_PROTOCOL,
+ SAFE_GUID))
+ {
+ printk(KERN_INFO"%s: not found\n", __FUNCTION__);
+ return -1;
+ }
+
+ //printk(KERN_INFO"%s: found bmDataCapabilities: %02x\n", __FUNCTION__, priv->bmDataCapabilities);
+
+ priv->comm_interface = priv->data_interface = 0;
+ priv->usbdnet_device_type = usbdnet_safe;
+ return 0;
+}
+
+/*
+ * See if we can have a BASIC interface:
+ *
+ * 1. class VENDOR_SPECIFIC_CLASS
+ * 2. subclass LINEO_INTERFACE_SUBCLASS_SAFENET
+ * 3. protocol LINEO_SAFENET_CRC
+ *
+ */
+STATIC int verify_basic_interface(
+ struct usb_device *device,
+ struct private *priv,
+ struct usb_interface *data_interface
+ )
+{
+ if (find_interface_data(priv, data_interface,
+ VENDOR_SPECIFIC_CLASS,
+ LINEO_INTERFACE_SUBCLASS_SAFENET,
+ LINEO_SAFENET_CRC,
+ 1))
+ {
+ printk(KERN_INFO"%s: not found\n", __FUNCTION__);
+ return -1;
+ }
+
+ //printk(KERN_INFO"%s: found\n", __FUNCTION__);
+
+ priv->comm_interface = priv->data_interface = 0;
+ priv->usbdnet_device_type = usbdnet_basic;
+ return 0;
+}
+
+
+/*
+ * Iterate across configurations looking for one that we like.
+ */
+STATIC int find_valid_configuration(struct usb_device *usbdev, struct private *priv)
+{
+ int configuration_number;
+
+ // We will try each and every possible configuration
+ for ( configuration_number = 0;
+ configuration_number < usbdev->descriptor.bNumConfigurations;
+ configuration_number++ )
+ {
+
+ //struct usb_config_descriptor *configuration = usbdev->config + configuration_number;
+ struct usb_config_descriptor *configuration = USB_DEV2CONFIG(usbdev,configuration_number);
+
+ //printk(KERN_INFO"%s[%d] bConfigurationValue: %d bNumInterfaces: %d\n", __FUNCTION__,
+ // configuration_number, configuration->bConfigurationValue, configuration->bNumInterfaces);
+
+ priv->configuration_number = configuration_number;
+ priv->bConfigurationValue = configuration->bConfigurationValue;
+
+ priv->comm_bInterfaceNumber = priv->comm_bAlternateSetting = -1;
+ priv->data_bInterfaceNumber = priv->data_bAlternateSetting = -1;
+ priv->nodata_bInterfaceNumber = priv->nodata_bAlternateSetting = -1;
+
+ /*
+ * CDC and RNDIS devices have two interfaces,
+ * MDLM and simple devices have a single interface,
+ * we don't handle any devices with more or less than one or two intefaces.
+ */
+ //printk(KERN_INFO"%s: interface(s) %d\n", __FUNCTION__, configuration->bNumInterfaces);
+ switch(configuration->bNumInterfaces) {
+
+ /*
+ * Tests for devices with two interfaces, e.g. CDC and RNDIS.
+ *
+ * We cannot guarantee which order the comm and data interfaces will be so we have to check for
+ * comm/data or data/comm.
+ */
+ case 2: {
+ //struct usb_interface *int0 = configuration->interface + 0;
+ //struct usb_interface *int1 = configuration->interface + 1;
+ struct usb_interface *int0 = USB_CONFIG2IFC(configuration,0);
+ struct usb_interface *int1 = USB_CONFIG2IFC(configuration,1);
+
+ if ( !verify_ethernet_comm_interface(usbdev, priv, 0, int0, 1, int1)) {
+ //printk(KERN_INFO"%s: cdc 0 1\n", __FUNCTION__);
+ return 0;
+ }
+ else if ( !verify_ethernet_comm_interface(usbdev, priv, 1, int1, 0, int0)) {
+ //printk(KERN_INFO"%s: cdc 1 0\n", __FUNCTION__);
+ return 0;
+ }
+ else if ( !verify_rndis_comm_interface(usbdev, priv, 0, int0, 1, int1)) {
+ //printk(KERN_INFO"%s: rndis 0 1\n", __FUNCTION__);
+ return 0;
+ }
+ else if ( !verify_rndis_comm_interface(usbdev, priv, 1, int1, 0, int0)) {
+ //printk(KERN_INFO"%s: rndis 1 0\n", __FUNCTION__);
+ return 0;
+ }
+ }
+ break;
+
+ /*
+ * tests for devices with a single interface, e.g. MDLM-SAFE MDLM-BLAN or BASIC
+ */
+ case 1:
+
+ if ( !verify_blan_interface(usbdev, priv, USB_CONFIG2IFC(configuration,0))) {
+ //printk(KERN_INFO"%s[%d] bConfigurationValue: %d bNumInterfaces: %d\n", __FUNCTION__,
+ // configuration_number, priv->bConfigurationValue, configuration->bNumInterfaces);
+ //printk(KERN_INFO"%s: mdlm-blan\n", __FUNCTION__);
+ return 0;
+ }
+ else if ( !verify_safe_interface(usbdev, priv, USB_CONFIG2IFC(configuration,0))) {
+ //printk(KERN_INFO"%s[%d] bConfigurationValue: %d bNumInterfaces: %d\n", __FUNCTION__,
+ // configuration_number, priv->bConfigurationValue, configuration->bNumInterfaces);
+ //printk(KERN_INFO"%s: mdlm-safe\n", __FUNCTION__);
+ return 0;
+ }
+ else if ( !verify_basic_interface(usbdev, priv, USB_CONFIG2IFC(configuration,0))) {
+ //printk(KERN_INFO"%s: basic\n", __FUNCTION__);
+ return 0;
+ }
+ break;
+
+ /*
+ * currently we have no support for devices with zero or more than
+ * two interfaces....
+ */
+ default:
+ break;
+ }
+ priv->configuration_number = priv->bConfigurationValue = 0;
+ }
+
+ // None of the configurations suited us.
+ //printk(KERN_INFO"%s: nothing found\n", __FUNCTION__);
+ return -1;
+}
+
+/*
+ * check the active configuration to see if there are any claimed interfaces
+ */
+STATIC int verify_no_claimed_interfaces(struct usb_config_descriptor *act_config)
+{
+ struct usb_interface *interface;
+ int interface_number;
+
+ // Go through all the interfaces and make sure none are
+ // claimed by anybody else.
+ //
+
+ //printk(KERN_INFO"%s:\n", __FUNCTION__);
+ if (!act_config) {
+ printk(KERN_INFO"%s: NULL\n", __FUNCTION__);
+ return 0;
+ }
+
+ //printk(KERN_INFO"%s: bNumInterfaces: %d\n", __FUNCTION__, act_config->bNumInterfaces);
+ for (interface_number = 0; interface_number < act_config->bNumInterfaces; interface_number++) {
+
+
+ interface = USB_CONFIG2IFC(act_config,interface_number);
+
+#if 0
+ if (usb_interface_claimed(interface)) {
+ printk(KERN_INFO"%s: failed\n", __FUNCTION__);
+ return -1;
+ }
+#endif
+ }
+ //printk(KERN_INFO"%s: ok\n", __FUNCTION__);
+ return 0;
+}
+
+#if defined(CONFIG_USB_USBLAN) || defined(CONFIG_USB_USBLAN_MODULE)
+/********************************************************
+ * In the 2.6 host system, the result of "probe" is a
+ * ERROR value, (0 for success, negative for failure)
+ * The actual value of priv is not passed to the caller
+ * of probe (usb_probe_interface)
+ *****************************************************/
+STATIC int usblan_probe_return(struct private *priv){
+ return ((priv == 0) ? -1 : 0);
+}
+
+#else
+
+#error
+/********************************************************
+ * In the 2.4 host system, the probe routine is called
+ * by usb_find_interface_driver. If probe() returns a
+ * non zero pointer, that value is passed along to
+ * usb_claim_interface()
+ *******************************************************/
+STATIC void * usblan_probe_return(struct private *priv){
+ return priv;
+}
+
+#error
+
+#endif
+
+#if defined(CONFIG_USB_USBLAN) || defined(CONFIG_USB_USBLAN_MODULE)
+STATIC int usblan_probe(struct usb_interface * udev, const struct usb_device_id *id){
+ struct private *priv = NULL;
+ struct usb_device_descriptor *device = NULL;
+ struct usb_device *usbdev;
+
+ /* find the usb device for the usb_interface */
+ usbdev = interface_to_usbdev(udev);
+ /* find the usb device descriptor */
+ device = &usbdev->descriptor;
+ //We can now resume the old probe routine except when returning a value
+#else
+#error
+STATIC void *
+usblan_probe(struct usb_device *usbdev, unsigned int ifnum, const struct usb_device_id *id)
+{
+ struct private *priv = NULL;
+ struct usb_device_descriptor *device = &usbdev->descriptor;
+
+#endif
+
+ /* Begin the common portion of the probe routine */
+ //printk(KERN_INFO "%s: probe\n", __FUNCTION__);
+
+ /* do we have a valid device structure, is the device a CDC device, is the product_id and
+ * vendor_id one that we are supposed to handle, has anyone else claimed any interfaces?
+ */
+ THROW_IF (device->bDeviceClass != CDC_DEVICE_CLASS, error);
+ THROW_IF (!idp_search(device->idVendor, device->idProduct), error);
+#if defined(LINUX24)
+ THROW_IF (verify_no_claimed_interfaces(USB_CONFIG2DESC(usbdev->actconfig)), error);
+#endif
+
+ /* create a private structure to save state information about this usb device.
+ */
+ THROW_IF (!(priv = create_private(usbdev->devnum)), error);
+ THROW_IF (find_valid_configuration(usbdev, priv), error);
+
+ /*
+ * There is a valid configuration.
+ */
+ //printk(KERN_INFO"%s: configuration_number: %d bConfigurationValue: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->bConfigurationValue);
+
+ THROW_IF (register_netdev(&priv->net), free_priv);
+
+
+#if !defined(CONFIG_USB_USBLAN) && !defined(CONFIG_USB_USBLAN_MODULE)
+ // usb_set_config() is not available inside probe() for 2.6.
+ //printk(KERN_INFO"%s: setting configuration bConfigurationValue: %d\n", __FUNCTION__, priv->bConfigurationValue);
+
+ THROW_IF ( usb_set_configuration( usbdev, priv->bConfigurationValue ), unregister );
+#else
+ // Claim the interfaces we want before starting to operate on them...
+ // manually claim the interfaces we want.
+ switch (priv->usbdnet_device_type ) {
+ case usbdnet_cdc:
+ case usbdnet_rndis:
+ //printk(KERN_INFO"%s: claiming comm interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->comm_interface);
+
+ usb_driver_claim_interface( &usblan_driver,
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->comm_interface), priv);
+ priv->intf_count += 1;
+
+ /* FALL THROUGH */ // In order to claim the data interface too.
+ case usbdnet_safe:
+ case usbdnet_basic:
+ case usbdnet_blan:
+ //printk(KERN_INFO"%s: claiming data interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->data_interface);
+
+ usb_driver_claim_interface( &usblan_driver,
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->data_interface),
+ priv );
+ priv->intf_count += 1;
+
+ break;
+ case usbdnet_unknown:
+ THROW(unregister);
+ }
+ priv->intf_max = priv->intf_count;
+#endif
+
+ //printk(KERN_INFO"%s: %s\n", __FUNCTION__, usbdnet_device_names[priv->usbdnet_device_type]);
+
+ /*
+ * If we have a nodata interface interface use it, otherwise use the normal data interface.
+ * MDLM and Safe devices never have a nodata interface.
+ */
+ switch (priv->usbdnet_device_type ) {
+ case usbdnet_cdc:
+ case usbdnet_rndis:
+
+ //printk(KERN_INFO"%s: setting comm interface bInterfaceNumber: %d bAlternateSetting: %d\n", __FUNCTION__,
+ // priv->comm_bInterfaceNumber, priv->comm_bAlternateSetting);
+ THROW_IF (usb_set_interface(usbdev, priv->comm_bInterfaceNumber, priv->comm_bAlternateSetting), unregister);
+
+ if (priv->nodata_bInterfaceNumber >= 0) {
+ //printk(KERN_INFO"%s: setting nodata interface bInterfaceNumber: %d bAlternateSetting: %d\n",
+ // __FUNCTION__, priv->nodata_bInterfaceNumber, priv->nodata_bAlternateSetting);
+ THROW_IF (usb_set_interface( usbdev, priv->nodata_bInterfaceNumber, priv->nodata_bAlternateSetting),
+ unregister);
+ }
+ else {
+ /* FALL THROUGH */
+
+ //printk(KERN_INFO"%s: setting data interface bInterfaceNumber: %d bAlternateSetting: %d\n",
+ // __FUNCTION__, priv->data_bInterfaceNumber, priv->data_bAlternateSetting);
+ THROW_IF (usb_set_interface( usbdev, priv->data_bInterfaceNumber, priv->data_bAlternateSetting),
+ unregister);
+ }
+ break;
+
+ case usbdnet_safe:
+ case usbdnet_blan:
+ case usbdnet_basic:
+
+ break;
+
+ case usbdnet_unknown:
+ THROW(unregister);
+ }
+
+
+#if !defined(CONFIG_USB_USBLAN) && !defined(CONFIG_USB_USBLAN_MODULE)
+ // manually claim the interfaces we want.
+ switch (priv->usbdnet_device_type ) {
+ case usbdnet_cdc:
+ case usbdnet_rndis:
+ //printk(KERN_INFO"%s: claiming comm interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->comm_interface);
+
+ usb_driver_claim_interface( &usblan_driver,
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->comm_interface), priv);
+ //&(usbdev->config[priv->configuration_number].interface[priv->comm_interface]), priv );
+ priv->intf_count += 1;
+
+ /* FALL THROUGH */ // In order to claim the data interface too.
+ case usbdnet_safe:
+ case usbdnet_basic:
+ case usbdnet_blan:
+ //printk(KERN_INFO"%s: claiming data interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->data_interface);
+
+ usb_driver_claim_interface( &usblan_driver,
+ //&(usbdev->config[priv->configuration_number].interface[priv->data_interface]),
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->data_interface),
+ priv );
+ priv->intf_count += 1;
+
+ break;
+ case usbdnet_unknown:
+ THROW(unregister);
+ }
+ priv->intf_max = priv->intf_count;
+#endif
+
+
+ skb_queue_head_init(&priv->rxq);
+ skb_queue_head_init(&priv->txq);
+ skb_queue_head_init(&priv->unlink);
+ skb_queue_head_init(&priv->done);
+
+ //printk(KERN_INFO "%s: tx_size: %3d rx_size: %3d\n", __FUNCTION__, priv->data_ep_out_size, priv->data_ep_in_size);
+ //printk(KERN_INFO "%s: tx_ep : %3x rx_ep : %3x\n", __FUNCTION__, priv->data_ep_out, priv->data_ep_in);
+
+ priv->usbdev = usbdev;
+
+#if defined(LINUX24)
+ // ctrl task for clear halt operation
+ priv->ctrl_task.routine = ctrl_task;
+ priv->ctrl_task.data = priv;
+ priv->ctrl_task.sync = 0;
+#else
+ PREPARE_WORK_ITEM(priv->ctrl_task, ctrl_task, priv);
+#endif
+
+ // reset task for reseting device
+#if defined(LINUX26)
+ PREPARE_WORK_ITEM(priv->reset_task, reset_task, priv);
+#else
+ priv->reset_task.routine = reset_task;
+ priv->reset_task.data = priv;
+ priv->reset_task.sync = 0;
+#endif
+
+ // unlink task for reseting device
+#if defined(LINUX26)
+ PREPARE_WORK_ITEM(priv->unlink_task, unlink_task, priv);
+#else
+ priv->unlink_task.routine = unlink_task;
+ priv->unlink_task.data = priv;
+ priv->unlink_task.sync = 0;
+#endif
+
+ // bottom half processing tasklet
+ priv->bh.func = bh;
+ priv->bh.data = (unsigned long) priv;
+
+ // init mutex and list, add to device list
+ init_MUTEX(&priv->mutex);
+ INIT_LIST_HEAD(&priv->list);
+
+ mutex_lock(&usbd_mutex);
+ list_add(&priv->list, &usbd_list);
+ mutex_unlock(&usbd_mutex);
+
+ //printk(KERN_INFO "%s: success %s\n", __FUNCTION__, DRIVER_VERSION);
+
+ switch (priv->usbdnet_device_type ) {
+ case usbdnet_blan:
+ network_notify(priv, NETWORK_ADDR_HOST, NETWORK_MASK, NETWORK_ADDR_CLIENT);
+ network_attach(&priv->net, NETWORK_ADDR_HOST, NETWORK_MASK, 1);
+ break;
+ default:
+ break;
+ }
+
+
+ // start as if the link is up
+ netif_device_attach(&priv->net);
+
+//#define NETWORK_ADDR_HOST 0xac100005 /* 172.16.0.0 */
+//#define NETWORK_ADDR_CLIENT 0xac100006 /* 172.16.0.0 */
+//#define NETWORK_MASK 0xfffffffc
+
+#if defined(CONFIG_USB_USBLAN) || defined(CONFIG_USB_USBLAN_MODULE)
+ usb_get_dev(usbdev);
+#else
+ usb_inc_dev_use(usbdev);
+#endif
+
+ usb_set_intfdata(udev, priv);
+
+ CATCH(error) {
+ printk(KERN_ERR"%s: caught error\n", __FUNCTION__);
+
+ CATCH(free_priv) {
+
+ printk(KERN_ERR"%s: caught free_priv\n", __FUNCTION__);
+
+ CATCH(unregister) {
+
+ printk(KERN_ERR"%s: caught unregister\n", __FUNCTION__);
+
+ printk(KERN_ERR"%s: unregister\n", __FUNCTION__);
+ unregister_netdev(&priv->net);
+ }
+
+ printk(KERN_ERR"%s: free priv\n", __FUNCTION__);
+ kfree(priv);
+ }
+ printk(KERN_ERR"%s: return NULL\n", __FUNCTION__);
+ return usblan_probe_return(NULL);
+ }
+ //printk(KERN_ERR"%s: return %p\n", __FUNCTION__, priv);
+ return usblan_probe_return(priv);
+}
+
+#if defined(CONFIG_USB_USBLAN) || defined(CONFIG_USB_USBLAN_MODULE)
+STATIC void usblan_disconnect(struct usb_interface *intf)
+{
+ struct private *priv = (struct private *) usb_get_intfdata(intf);
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+
+ /* If we claimed more than one interface during probe, this will
+ be called more than once. */
+ if (priv->intf_count == priv->intf_max) {
+ // This is the first interface to be disconnected, unregister the network interface
+ unregister_netdev(&priv->net);
+
+ // remove from device list
+ mutex_lock(&usbd_mutex);
+ mutex_lock(&priv->mutex);
+ list_del(&priv->list);
+ mutex_unlock(&usbd_mutex);
+ }
+ // release interfaces
+
+ //printk(KERN_INFO"%s: releasing interface: %p from configuration: %d\n", __FUNCTION__, intf, priv->configuration_number);
+ // XXX usb_driver_release_interface(&usblan_driver,intf);
+ //printk(KERN_INFO"%s: releasing interface: ok\n", __FUNCTION__);
+ priv->intf_count -= 1;
+
+ if (priv->intf_count <= 0) {
+ // disable
+ usb_put_dev(usbdev);
+
+ // destroy the network interface and free the private storage
+ kfree(priv);
+ }
+}
+#else
+STATIC void usblan_disconnect(struct usb_device *usbdev, void *ptr) {
+ struct private *priv = (struct private *) ptr;
+
+ // unregister the network interface
+ unregister_netdev(&priv->net);
+
+ // remove from device list
+ mutex_lock(&usbd_mutex);
+ mutex_lock(&priv->mutex);
+ list_del(&priv->list);
+ mutex_unlock(&usbd_mutex);
+
+ // release interfaces
+
+
+ switch (priv->usbdnet_device_type ) {
+ case usbdnet_cdc:
+ case usbdnet_rndis:
+ //printk(KERN_INFO"%s: releasing comm interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->comm_interface);
+ usb_driver_release_interface( &usblan_driver,
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->comm_interface));
+// &(usbdev->config[priv->configuration_number].interface[priv->comm_interface]));
+ /* FALL THROUGH */
+ case usbdnet_safe:
+ case usbdnet_basic:
+ case usbdnet_blan:
+ //printk(KERN_INFO"%s: releasing data interface: configuration: %d interface: %d\n", __FUNCTION__,
+ // priv->configuration_number, priv->data_interface);
+ usb_driver_release_interface( &usblan_driver,
+ USB_CONFIG2IFC(USB_DEV2CONFIG(usbdev,priv->configuration_number),priv->data_interface));
+// &(usbdev->config[priv->configuration_number].interface[priv->data_interface]));
+ case usbdnet_unknown:
+ break;
+ }
+
+
+ // disable
+ usb_dec_dev_use(usbdev); // QQSV
+
+ // destroy the network interface and free the private storage
+ kfree(priv);
+}
+#endif
+
+
+static struct usb_driver usblan_driver = {
+ name: "usblan",
+ probe: usblan_probe,
+ disconnect: usblan_disconnect, // QQSV
+ id_table: id_table,
+};
+
+
+/* Module loading functions - modinit and modexit*********************************************** */
+
+/*
+ * usbdnet_modinit - module init
+ *
+ */
+STATIC int __init usbdnet_modinit(void)
+{
+ //USB_PROBE_SET(usblan_driver, probe);
+ //printk(KERN_INFO DRIVER_VERSION " " DRIVER_AUTHOR "XXX\n");
+
+
+ info(DRIVER_DESC " " DRIVER_VERSION " " DRIVER_AUTHOR);
+ //info(DRIVER_DESC);
+
+ get_random_bytes(default_addr, ETH_ALEN);
+ default_addr[0] = (default_addr[0] & 0xf0) | 0x02;
+
+#if defined(LONG_STRING_OF_ZEROES_HACK)
+ fermat_init();
+#endif
+
+ // if we have vendor_id / product_id parameters patch them into id list
+ if (vendor_id && product_id) {
+ int i;
+ //printk(KERN_INFO "%s: vendor_id: %x product_id: %x\n", __FUNCTION__, vendor_id, product_id);
+ for (i = 0; i < (sizeof(id_table) / sizeof(struct usb_device_id) - 1); i++) {
+
+ if (id_table[i].idVendor == vendor_id && id_table[i].idProduct == product_id) {
+
+ //printk(KERN_INFO "%s: vendor_id: %x product_id: %x already in table\n",
+ // __FUNCTION__, vendor_id, product_id);
+ break;
+ }
+ if (!id_table[i].idVendor && !id_table[i].idProduct) {
+ //printk(KERN_INFO "%s: vendor_id: %x product_id: %x inserted into table\n",
+ // __FUNCTION__, vendor_id, product_id);
+ id_table[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
+ id_table[i].idVendor = vendor_id;
+ id_table[i].idProduct = product_id;
+ id_table[i].bDeviceClass = class;
+ id_table[i].bDeviceSubClass = subclass;
+ break;
+ }
+ }
+ }
+
+ // register us with the usb core layer
+ if (usb_register(&usblan_driver)) {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+/*
+ * function_exit - module cleanup
+ *
+ */
+STATIC void __exit usbdnet_modexit(void) {
+ // de-register from the usb core layer
+ usb_deregister(&usblan_driver);
+}
+
+module_init(usbdnet_modinit);
+module_exit(usbdnet_modexit);
diff --git a/drivers/usb/usblan/usblan.h b/drivers/usb/usblan/usblan.h
new file mode 100644
index 000000000000..0e92f3df2c6c
--- /dev/null
+++ b/drivers/usb/usblan/usblan.h
@@ -0,0 +1,84 @@
+#ifndef _USB_NET_USBLAN_H
+#define _USB_NET_USBLAN_H 1
+
+#define USB_NEW_COMPLETE_T
+//#define USB_ALLOC_URB(packets) usb_alloc_urb(packets, GFP_KERNEL)
+#define USB_ALLOC_URB(packets,mem_flags) usb_alloc_urb(packets,mem_flags)
+
+#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
+ usb_fill_bulk_urb(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,(usb_complete_t)COMPLETE,CONTEXT)
+
+#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
+ usb_fill_control_urb(URB,DEV,PIPE,SETUP,TRANSFER_BUFFER,BUFFER_LENGTH,(usb_complete_t)COMPLETE,CONTEXT)
+
+#define USB_SUBMIT_URB(urb) usb_submit_urb(urb, 0)
+#define USB_ALTSETTING(ifc,alt_index) &(ifc->altsetting[alt_index].desc)
+#define USB_IFC2EP(ifc,index) &(container_of(ifc, struct usb_host_interface, desc)->endpoint[index].desc)
+#define USB_IFC2HOST(ifc) container_of(ifc, struct usb_host_interface, desc)
+#define USB_DEV2CONFIG(dev,index) &(dev->config[index].desc)
+#define USB_CONFIG2IFC(cfg, index) (container_of(cfg, struct usb_host_config, desc)->interface[index])
+#define USB_CONFIG2DESC(cfg) &(cfg->desc)
+#define USB_PROBE_DELAYED_SET NULL
+#define USB_PROBE_ERROR -1
+#define USB_PROBE_SET(driver,proc) { \
+ int __usb_probe_proc(struct usb_interface *intf, \
+ const struct usb_device_id *id){\
+ struct usb_device *usbdev = NULL;\
+ if(!intf) { return USB_PROBE_ERROR; }\
+ usbdev = interface_to_usbdev(intf); \
+ return(!((*proc)(usbdev,0,id)) ? USB_PROBE_ERROR :0);\
+ }\
+ driver->probe = __usb_probe_proc; \
+}
+
+
+
+//Missing (obsolete??) XXX
+//static void usb_inc_dev_use(struct usb_device * dev) {}
+#define usb_inc_dev_use(dev) usb_get_dev(dev)
+#define usb_dec_dev_use(dev) usb_put_dev(dev)
+/*----------------------------------------------------------------------------*
+ * New USB Structures *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * urb->transfer_flags:
+ */
+#define USB_DISABLE_SPD 0x0001
+//#define URB_SHORT_NOT_OK USB_DISABLE_SPD
+#define USB_ISO_ASAP 0x0002
+#define USB_ASYNC_UNLINK 0x0008
+#define USB_QUEUE_BULK 0x0010
+#define USB_NO_FSBR 0x0020
+#define USB_ZERO_PACKET 0x0040 // Finish bulk OUTs always with zero length packet
+#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */
+ /* ... less overhead for QUEUE_BULK */
+#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD!
+
+/*
+ * USB-status codes:
+ * USB_ST* maps to -E* and should go away in the future
+ */
+
+#define USB_ST_NOERROR 0
+#define USB_ST_CRC (-EILSEQ)
+#define USB_ST_BITSTUFF (-EPROTO)
+#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */
+#define USB_ST_DATAOVERRUN (-EOVERFLOW)
+#define USB_ST_DATAUNDERRUN (-EREMOTEIO)
+#define USB_ST_BUFFEROVERRUN (-ECOMM)
+#define USB_ST_BUFFERUNDERRUN (-ENOSR)
+#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */
+#define USB_ST_SHORT_PACKET (-EREMOTEIO)
+#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */
+#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */
+#define USB_ST_URB_PENDING (-EINPROGRESS)
+#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */
+#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/
+#define USB_ST_NOTSUPPORTED (-ENOSYS)
+#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */
+#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */
+#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */
+#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/
+
+#endif
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 5b3dbcfcda48..62e578894a93 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -337,6 +337,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
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 83e02b3429b6..61ef5a502f6c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -108,6 +108,7 @@ obj-$(CONFIG_FB_IMX) += imxfb.o
obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/
obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
+obj-$(CONFIG_FB_MXC) += mxc/
obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 9609a6c676be..d00e50da4427 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -90,3 +90,26 @@ config BACKLIGHT_CARILLO_RANCH
help
If you have a Intel LE80578 (Carillo Ranch) 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
+ default y
+
+config BACKLIGHT_MXC_LCDC
+ tristate "LCDC PWM Backlight Driver"
+ depends on BACKLIGHT_MXC && (ARCH_MX21 || ARCH_MX27)
+ default y
+
+config BACKLIGHT_MXC_PMIC
+ tristate "PMIC Backlight Driver"
+ depends on BACKLIGHT_MXC && MXC_MC13783_LIGHT && MXC_MC13783_POWER
+ default y
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 965a78b18118..9280ca44c38d 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -9,3 +9,7 @@ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.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
diff --git a/drivers/video/backlight/mxc_ipu_bl.c b/drivers/video/backlight/mxc_ipu_bl.c
new file mode 100644
index 000000000000..1e911f4c7ff5
--- /dev/null
+++ b/drivers/video/backlight/mxc_ipu_bl.c
@@ -0,0 +1,156 @@
+/*
+ * 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 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 <asm/arch/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 = class_get_devdata(&bd->class_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 = class_get_devdata(&bd->class_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);
+}
+
+module_init(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..1e014ced715f
--- /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 = class_get_devdata(&bd->class_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 = class_get_devdata(&bd->class_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_pmic_bl.c b/drivers/video/backlight/mxc_pmic_bl.c
new file mode 100644
index 000000000000..9df4f133185b
--- /dev/null
+++ b/drivers/video/backlight/mxc_pmic_bl.c
@@ -0,0 +1,197 @@
+/*
+ * 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 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 <asm/arch/pmic_power.h>
+#include <asm/arch/pmic_light.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 = class_get_devdata(&bd->class_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 = class_get_devdata(&bd->class_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/mxc/Kconfig b/drivers/video/mxc/Kconfig
new file mode 100644
index 000000000000..48be1104d85c
--- /dev/null
+++ b/drivers/video/mxc/Kconfig
@@ -0,0 +1,82 @@
+config FB_MXC
+ tristate "MXC Framebuffer support"
+ depends on FB && (MXC_IPU || ARCH_MX21 || ARCH_MX27)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ 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_TVOUT
+ bool "TV Out Encoder"
+ depends on FB_MXC_SYNC_PANEL
+ default n
+
+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"
+
+if MACH_I30030ADS
+config FB_MXC_EPSON_QVGA_PANEL
+ depends on FB_MXC_ASYNC_PANEL
+ default y
+ bool "Epson 240x320 Panel"
+endif
+
+if (MACH_MXC30030ADS || MACH_MXC30031ADS)
+config FB_MXC_TOSHIBA_QVGA_PANEL
+ depends on FB_MXC_ASYNC_PANEL
+ bool "Toshiba 240x320 Panel"
+
+config FB_MXC_SHARP_128_PANEL
+ depends on FB_MXC_ASYNC_PANEL
+ bool "Sharp 128x128 Panel"
+endif
+
+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..c0ef8adc404f
--- /dev/null
+++ b/drivers/video/mxc/Makefile
@@ -0,0 +1,12 @@
+ifeq ($(CONFIG_ARCH_MX21)$(CONFIG_ARCH_MX27),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
+ obj-$(CONFIG_FB_MXC_TVOUT) += fs453.o
+ obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxcfb.o mxcfb_modedb.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
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..1aac341d1c4c
--- /dev/null
+++ b/drivers/video/mxc/mx2fb.c
@@ -0,0 +1,1346 @@
+/*
+ * 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 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 <asm/uaccess.h>
+#include <asm/arch/mxcfb.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 perclk3, 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 */
+ perclk3 = clk_round_rate(lcdc_clk, 134000000);
+ if (clk_set_rate(lcdc_clk, perclk3)) {
+ printk(KERN_INFO "mx2fb: Unable to set clock to %lu\n",
+ perclk3);
+ perclk3 = clk_get_rate(lcdc_clk);
+ }
+
+ /* Calculate pixel clock divider, and round to the nearest integer */
+ pcd = (perclk3 * 8 / (PICOS2KHZ(var->pixclock) * 1000UL) + 4) / 8;
+ if (--pcd > 0x3F)
+ pcd = 0x3F;
+
+ /* Panel configuration register */
+ pcr = 0xFA008B80 | pcd;
+ pcr |= (var->sync & FB_SYNC_CLK_INVERT) ? 0x01000000 : 0;
+ pcr |= (var->sync & FB_SYNC_SHARP_MODE) ? 0x00000040 : 0;
+ pcr |= (var->sync & FB_SYNC_OE_ACT_HIGH) ? 0 : 0x00100000;
+ __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(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(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/mx2fb_epson.c b/drivers/video/mxc/mx2fb_epson.c
new file mode 100644
index 000000000000..3a613e5dbb26
--- /dev/null
+++ b/drivers/video/mxc/mx2fb_epson.c
@@ -0,0 +1,2173 @@
+#error "please port this file to linux 2.6.18"
+/*
+ * 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
+ */
+
+/*!@example slcdctest.c test driver using EPSON L2F50032T00 serial mode */
+
+/**
+ *
+ * @defgroup SLCDC SLCDC driver
+ **/
+/**@{*/
+/**
+ * @file mx2fb_epson.c
+ * @brief slcdc driver source code
+ *
+ * This is the basic release for SLCDC driver, which is to support EPSON
+ * L2F50032T00 16bit seria mode5-6-5 and parallel mode. This driver acts as a
+ * standard character device, which can be dinamically loaded.
+ * For example, you can refer to slcdctest.c file.
+ *
+ * Modification History:
+ * 15,Dec,2003 Karen Kang
+ *
+ * 21,Feb,2006 Vasanthan S
+ *
+ * @bug
+ **/
+
+#ifndef __KERNEL__
+# define __KERNEL__
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/pm.h>
+
+#include "../console/fbcon.h"
+#include "../../mxc/mempool/mempool.h"
+#include <linux/timer.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h> /* get_user,copy_to_user */
+#include <asm/irq.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/mx27.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include <linux/devfs_fs_kernel.h>
+
+#ifdef CONFIG_ARCH_MX2ADS
+#include <asm/arch/mx2.h>
+#endif
+
+#define MODULE_NAME "slcdc"
+
+#define DBMX_DEBUG 1
+#ifdef DBMX_DEBUG
+#define TRACE(fmt, args...) \
+ { \
+ printk("\n %s:%d:%s:",__FILE__, __LINE__,__FUNCTION__); \
+ printk(fmt, ## args);\
+ }
+#else
+#define TRACE(fmt, args...)
+#endif
+
+#define FAILED(fmt, args...) \
+ { \
+ printk("\n %s:%d:%s:",__FILE__, __LINE__,__FUNCTION__); \
+ printk(fmt, ## args);\
+ }
+
+#define INFO(fmt, args...) \
+ { \
+ printk("\n"); \
+ printk(fmt, ## args);\
+ }
+
+/*@brief definition for SLCDC_LCD_TXCONFIG */
+#define SLCDC_IMG_8L 0x00020000
+
+/*@name SLCDC connection configuration
+ *@brief config macro for serial mode or parallel mode. SLCDC can operate in
+ * serial or parallel mode. This macro reflects the hardware configuration
+ * and is not a software configuration.
+ */
+/**@{*/
+#define SLCDC_SERIAL_MODE
+/* #define SLCDC_PARALLE_MODE */
+/**@}*/
+
+/*@name SLCDC IO MUX configuration
+ *@brief configuration macro for the pin mux detail. This tells which pins are
+ * configured for SLCDC. For more information refer processor data sheet.
+ * Most cases only one of the following macros should be enabled.
+ */
+/**@{*/
+#define SLCDC_MUX_SSI3_SLCDC2 /*!< 1. SLCDC is mux.ed with SSI3 */
+ //#define SLCDC_MUX_SD_SLCDC1 /*!< 2. SLCDC is mux.ed with SD2 */
+ //#define SLCDC_MUX_LCD_SLCDC1 /*!< 3. SLCDC is mux.ed with LCD */
+/**@}*/
+
+/*@brief configuration macro which tells whether the interrupt should be used
+ * or not for SLCDC. SLCDC will provide an interrupt on a completed
+ * transfer, which can be used for refreshing the data.
+ */
+#define USING_INTERRUPT_SLCDC
+
+/**
+ *@name ioctl command macro definitions
+ *@brief definition for SLCDC ioctl cmd,these command definition is different
+ * for serial \nmode and paralel mode ,in this driver, we have not
+ * supported all these commands, \n please check the ioctl function for
+ * details.
+ **/
+/**@{*/
+/* definition for SLCDC cmd */
+/* same as Epson 10043 & 50052 */
+#ifdef SLCDC_SERIAL_MODE
+#define SLCDC_CMD_DISON 0xaf00 /*!< 1. display on */
+#define SLCDC_CMD_DISOFF 0xae00 /*!< 2. display off */
+#define SLCDC_CMD_DISNOR 0xa600 /*!< 3. normal display */
+#define SLCDC_CMD_DISINV 0xa700 /*!< 4. inverse display */
+#define SLCDC_CMD_DISCTL 0xca00 /*!< 5. display control */
+#define SLCDC_CMD_SLPIN 0x9500 /*!< 10.sleep in */
+#define SLCDC_CMD_SLPOUT 0x9400 /*!< 11.sleep out */
+#define SLCDC_CMD_SD_PSET 0x7500 /*!< 12.page address set */
+#define SLCDC_CMD_SD_CSET 0x1500 /*!< 14.column address set */
+
+#define SLCDC_CMD_DATCTL 0xbc /*!< 16.data scan direction, etc. */
+#define SLCDC_CMD_RAMWR 0x5c00 /*!< 17.writing to memory */
+#define SLCDC_CMD_PTLIN 0xa800 /*!< 19.partial display in */
+#define SLCDC_CMD_PTLOUT 0xa900 /*!< 20.partial display out */
+
+/* value different from 10043 but same as 50052 */
+#define SLCDC_CMD_VOLCTR 0xc600 /*!< 25.Electronic volume control */
+
+/* commands not found in 10043 but in 50052*/
+#define SLCDC_CMD_GCP64 0xcb00 /*!< 6. 64 grayscale pulse positon set */
+#define SLCDC_CMD_GCP16 0xcc00 /*!< 7. 16 grayscale pulse positon set */
+#define SLCDC_CMD_GSSET 0xcd00 /*!< 8. grayscale set */
+#define SLCDC_CMD_RAMRD 0x5d00 /*!< 18.memory read */
+#define SLCDC_CMD_ASCSET 0xaa00 /*!< 21.area scroll set */
+#define SLCDC_CMD_SCSTART 0xab00 /*!< 22.scroll start set */
+#define SLCDC_CMD_EPCTIN 0x6100 /*!< 26.Power IC control for EVR */
+#define SLCDC_CMD_EPCTOUT 0x6200 /*!< 27.Power IC control for EVR */
+
+#else
+#define SLCDC_CMD_DISON 0xaf /*!<1. display on */
+#define SLCDC_CMD_DISOFF 0xae /*!<2. display off */
+#define SLCDC_CMD_DISNOR 0xa6 /*!<3. normal display */
+#define SLCDC_CMD_DISINV 0xa7 /*!<4. inverse display */
+#define SLCDC_CMD_DISCTL 0xca /*!<5. display control */
+#define SLCDC_CMD_SLPIN 0x95 /*!<10.sleep in */
+#define SLCDC_CMD_SLPOUT 0x94 /*!<11.sleep out */
+#define SLCDC_CMD_SD_PSET 0x75 /*!<12.page address set */
+#define SLCDC_CMD_SD_CSET 0x15 /*!<14.column address set */
+
+#define SLCDC_CMD_DATCTL 0xbc /*!<16.data scan direction, etc. */
+//#define SLCDC_CMD_DATCTL 0xbc00 /*!<16.data scan direction, etc.*/
+#define SLCDC_CMD_RAMWR 0x5c /*!<17.writing to memory */
+#define SLCDC_CMD_PTLIN 0xa8 /*!<19.partial display in */
+#define SLCDC_CMD_PTLOUT 0xa9 /*!<20.partial display out */
+
+/* value different from 10043 but same as 50052 */
+#define SLCDC_CMD_VOLCTR 0xc6 /*!<25.Electronic volume control */
+
+/* commands not found in 10043 but in 50052*/
+#define SLCDC_CMD_GCP64 0xcb /*!<6. 64 grayscale pulse positon set */
+#define SLCDC_CMD_GCP16 0xcc /*!<7. 16 grayscale pulse positon set */
+#define SLCDC_CMD_GSSET 0xcd /*!<8. grayscale set */
+#define SLCDC_CMD_RAMRD 0x5d /*!<18.memory read */
+#define SLCDC_CMD_ASCSET 0xaa /*!<21.area scroll set */
+#define SLCDC_CMD_SCSTART 0xab /*!<22.scroll start set */
+#define SLCDC_CMD_EPCTIN 0x61 /*!<26.Power IC control for EVR */
+#define SLCDC_CMD_EPCTOUT 0x62 /*!<27.Power IC control for EVR */
+#endif
+
+/**@}*/
+
+#define SLCDC_IRQ INT_SLCDC
+#define SLCDC_CMD_MEM_SIZE 4
+#define SLCDC_WIDTH 176
+#define SLCDC_HIGH 220
+#define SLCDC_BPP 16
+#define SLCDC_PIXEL_MEM_SIZE (SLCDC_WIDTH*SLCDC_HIGH*SLCDC_BPP)/8
+#define SLCDC_MEM_SIZE (SLCDC_WIDTH*SLCDC_HIGH)
+#define _SLCDC_DATA_SIZE_ (SLCDC_PIXEL_MEM_SIZE + 32)
+#define SLCDC_DATA_MEM_SIZE \
+ ((unsigned)(PAGE_ALIGN(_SLCDC_DATA_SIZE_ + PAGE_SIZE * 2)))
+
+//bit mask definition in STAT/CTRL register
+#define SLCDC_TRANSFER_BUSY 0x4
+#define SLCDC_TRANSFER_ERROR 0x10
+
+//<<<<<< Global Variable
+/*used for SLCDC data buffer */
+__attribute__ ((aligned(4)))
+u16 *g_slcdc_dbuffer_address;
+
+/* physical address for SLCDC data buffer*/
+__attribute__ ((aligned(4)))
+u16 *g_slcdc_dbuffer_phyaddress;
+
+/* used for SLCDC command buffer */
+__attribute__ ((aligned(4)))
+u16 *g_slcdc_cbuffer_address;
+
+/* physical address for SLCDC command buffer */
+__attribute__ ((aligned(4)))
+u16 *g_slcdc_cbuffer_phyaddress;
+
+static wait_queue_head_t slcdc_wait;
+static int slcdc_device_num;
+static int g_slcdc_status;
+
+static int slcdc_major;
+module_param(slcdc_major, int, 0444);
+MODULE_PARM_DESC(slcdc_major,
+ "slcdc char device major number. If this number is"
+ " set to zero, then automatic major number allocation will be done");
+
+static int slcdc_minor;
+module_param(slcdc_minor, int, 0444);
+MODULE_PARM_DESC(slcdc_minor, "slcdc char device minor number");
+
+#define SLCDC_OPEN_STATUS 0x0001
+#define SLCDC_SUSPEND_STATUS 0x0002
+
+#define SLCDC_MAJOR_NUM 10 /*!< SLCDC char dev default major number */
+#define SLCDC_MINOR_NUM 156 /*!< SLCDC char dev default minor number */
+
+#define FBCON_HAS_CFB4
+#define FBCON_HAS_CFB8
+#define FBCON_HAS_CFB16
+
+static struct cdev slcdc_dev;
+
+extern void gpio_slcdc_active(int type);
+extern void gpio_slcdc_inactive(int type);
+
+int slcdc_open(struct inode *inode, struct file *filp);
+int slcdc_release(struct inode *inode, struct file *filp);
+static int slcdc_ioctl(struct inode *inode, struct file *filp,
+ u_int cmd, u_long arg);
+static int slcdc_mmap(struct file *filp, struct vm_area_struct *vma);
+static void __init _init_fbinfo(void);
+
+typedef struct {
+ u16 *screen_start_address;
+ u16 *v_screen_start_address;
+} slcdc_par_t;
+
+slcdc_par_t slcdc_par;
+
+struct file_operations g_slcdc_fops = {
+ open:slcdc_open,
+ release:slcdc_release,
+ ioctl:slcdc_ioctl,
+ mmap:slcdc_mmap,
+};
+
+//>>>>>> Global Variable
+
+#ifdef DBMX_DEBUG
+#define FUNC_START TRACE(KERN_ERR"start of %s\n", __FUNCTION__);
+#define FUNC_END TRACE(KERN_ERR"end of %s\n", __FUNCTION__);
+#else
+#define FUNC_START
+#define FUNC_END
+#endif
+
+#define RED 0xf00
+#define GREEN 0xf0
+#define BLUE 0x0f
+
+/**@brief Local LCD controller parameters*/
+struct slcdcfb_par {
+ u_char *screen_start_address; /*!< Screen Start Address */
+ u_char *v_screen_start_address; /*!< Virtul Screen Start Address */
+ unsigned long screen_memory_size; /*!< Screen memory size */
+ unsigned int palette_size; /*!<Palette size */
+ unsigned int max_xres; /*!<Maximum x resolution */
+ unsigned int max_yres; /*!<Maximum x resolution */
+ unsigned int xres; /*!<X resolution */
+ unsigned int yres; /*!<Y resolution */
+ unsigned int xres_virtual; /*!<Vitual x resolution */
+ unsigned int yres_virtual; /*!<Vitual y resolution */
+ unsigned int max_bpp; /*!<Maximum bit per pixel */
+ unsigned int bits_per_pixel; /*!<Bits per pixel */
+ unsigned int currcon; /*!<Current console ID */
+ unsigned int visual; /*!<Vitual color type */
+ unsigned int TFT:1; /*!<TFT flag */
+ unsigned int color:1; /*!<Color flag */
+ unsigned int sharp:1; /*!< Sharp LCD flag */
+};
+
+/* Frame buffer device API */
+static int slcdcfb_set_var(struct fb_info *info);
+/* perform fb specific mmap */
+static int slcdcfb_mmap(struct fb_info *info, struct file *file,
+ struct vm_area_struct *vma);
+/* perform fb specific ioctl (optional) */
+static int slcdcfb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg,
+ struct fb_info *info);
+static int slcdcfb_open(struct fb_info *info, int user);
+static int slcdcfb_release(struct fb_info *info, int user);
+
+static int _check_var(struct fb_var_screeninfo *var, struct fb_info *info);
+static int _decode_var(struct fb_info *info);
+static int slcdcfb_blank(int blank, struct fb_info *info);
+
+/*
+ * Framebuffer file operations
+ */
+static struct fb_ops slcdcfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = slcdcfb_open,
+ .fb_release = slcdcfb_release,
+ .fb_check_var = _check_var,
+ .fb_set_par = _decode_var,
+ .fb_blank = slcdcfb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = soft_cursor,
+ .fb_ioctl = slcdcfb_ioctl,
+ .fb_mmap = slcdcfb_mmap,
+};
+
+static struct display global_disp; /* Initial (default) Display Settings */
+static struct fb_info slcdc_fb_info;
+static struct fb_var_screeninfo init_var;
+static struct slcdcfb_par current_par;
+static struct timer_list slcdc_timer;
+static int start_fb_timer_flag = 0; //0 is stop, 1 is start
+
+#define MIN_XRES 64
+#define MIN_YRES 64
+#define LCD_MAX_BPP SLCDC_BPP
+#define MAX_PIXEL_MEM_SIZE SLCDC_PIXEL_MEM_SIZE
+#define SLCDC_REFRESH_RATE (HZ / 100)
+
+#define SLCDC2_CLK_PC31 31 /* refer iMX27 Pin Mux details */
+#define SLCDC2_CS_PC30 30
+#define SLCDC2_RS_PC29 29
+#define SLCDC2_D0_PC28 28
+
+#define SLCDC1_CLK_PB5 5
+#define SLCDC1_CS_PB8 8
+#define SLCDC1_RS_PB7 7
+#define SLCDC1_D0_PB6 6
+
+#define SLCDC1_DAT0_PA6 6
+
+/* Fake monspecs to fill in fbinfo structure */
+static struct fb_monspecs monspecs __initdata = {
+ .hfmin = 30000,
+ .hfmax = 70000,
+ .vfmin = 50,
+ .vfmax = 65,
+ .dpms = 0 /* Generic */
+};
+
+void slcdc_delay(int num)
+{
+ udelay(num);
+}
+
+/**
+ *@brief slcdc gpio configure routine for serial mode
+ *
+ * Function Name: slcdc_gpio_serial
+ *
+ *
+ * Description:This routine will implement gpio configurations for serial mode
+ * both for\n LCDC mux and SDHC2 mux, you can use macro
+ * SLCDC_MUX_SSI3_SLCDC2 or SLCDC_MUX_SD_SLCDC1 \n to choose the
+ * right way according to your hardware configuration.
+ *
+ *
+ *@return None
+ *
+ * Modification History:
+ * Dec,2003 Karen update for MX21 TO2
+ * Jun,2004 Shirley update for LCDC mux
+ * Mar,2006 Update for MX27 mux
+ **/
+
+void slcdc_gpio_serial(void)
+{
+#ifdef SLCDC_MUX_SSI3_SLCDC2
+ /* we have to set SLCDC2_CLK, SLCDC2_CS, SLCDC2_RS and SLCDC2_D0 */
+ //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 */
+ gpio_slcdc_active(0);
+
+#endif
+#ifdef SLCDC_MUX_SD_SLCDC1
+ //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 */
+ gpio_slcdc_active(1);
+
+#endif
+}
+
+/**
+ *@brief slcdc gpio configure routine for parallel mode
+ *
+ * Function Name: slcdc_gpio_paralle
+ *
+ *
+ * Description:This routine will implement gpio configurations for parralel
+ * mode both for\n LCDC mux and SDHC2 mux, you can use macro
+ * SLCDC_MUX_SSI3_SLCDC2 or SLCDC_MUX_SD_SLCDC1 \n to choose the
+ * right way according to your hardware configuration.
+ *
+ *
+ *@return None
+ *
+ * Modification History:
+ * Jun,2004 Shirley update for LCDC mux
+ * Mar,2006 Update for MX27 mux
+ **/
+#ifndef SLCDC_MUX_SSI3_SLCDC2
+void slcdc_gpio_paralle(void)
+{
+#ifdef SLCDC_MUX_LCD_SLCDC1
+ /* Make sure the actual hardware connection is based on this, if other port
+ is used then the following code has to be modified accordingly.
+ For further details refer Pin Mux details in iMX27 Spec. */
+ /* 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);
+ */
+ gpio_slcdc_active(2);
+
+#endif
+}
+#endif
+
+void slcdc_reset(int level)
+{
+ if (level == 0) {
+#if 0
+#ifdef SLCDC_MUX_SSI3_SLCDC2
+ /* OE_ACD as a reset pin */
+ _reg_GPIO_GIUS(GPIOA) |= 0x80000000;
+ _reg_GPIO_OCR2(GPIOA) |= 0xc0000000;
+ _reg_GPIO_DDIR(GPIOA) |= 0x80000000;
+
+ /* set reset pin to low */
+ _reg_GPIO_DR(GPIOA) &= 0x7fffffff;
+#endif
+#ifdef SLCDC_MUX_SD_SLCDC1
+ /* SD2_D1 as reset pin */
+ _reg_GPIO_GIUS(GPIOB) |= 0x00000020;
+ _reg_GPIO_OCR1(GPIOB) |= 0x00000c00;
+ _reg_GPIO_DDIR(GPIOB) |= 0x00000020;
+
+ /* set reset pin to low */
+ _reg_GPIO_DR(GPIOB) &= 0xffffffdf;
+#endif
+ } else {
+#ifdef SLCDC_MUX_SSI3_SLCDC2
+ /* set reset pin to high */
+ _reg_GPIO_DR(GPIOA) |= 0x80000000;
+#endif
+#ifdef SLCDC_MUX_SD_SLCDC1
+ /* set reset pin to high */
+ _reg_GPIO_DR(GPIOB) |= 0x00000020;
+#endif
+#endif
+ }
+
+}
+
+/**
+ *@brief slcdc hardware initialization
+ *
+ * Function Name: slcdc_init_dev
+ *
+ * Description : This routine will enable the SLCDC and the clock for it
+ *
+ **/
+void slcdc_init_dev(void)
+{
+ volatile unsigned long reg;
+
+ reg = __raw_readl(IO_ADDRESS(MAX_BASE_ADDR + 0x100 * 3 + 0x10));
+ reg = reg | 0x00040000;
+ __raw_writel(reg, IO_ADDRESS(MAX_BASE_ADDR + 0x100 * 3 + 0x10));
+
+ reg = __raw_readl(IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x58)); /* _reg_SYS_PCSR */
+ reg = reg | 0x00000004; /* set LCD/SLCD bus master high priority */
+ __raw_writel(reg, IO_ADDRESS(SYSCTRL_BASE_ADDR + 0x58));
+
+ /* enable the slcd clk */
+ reg = __raw_readl(IO_ADDRESS(CCM_BASE_ADDR + 0x20)); /* _reg_CRM_PCCR0 */
+ reg |= 0x02200000;
+ __raw_writel(reg, IO_ADDRESS(CCM_BASE_ADDR + 0x20));
+}
+
+/**
+ *@brief slcdc register initialization
+ *
+ * Function Name: slcdc_init_reg
+ *
+ * Description:This routine will setup the SLCDC register for first time
+ *
+ **/
+void slcdc_init_reg(void)
+{
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x04)); /* _reg_SLCDC_DBUF_SIZE */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x08)); /* _reg_SLCDC_CBADDR */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x0C)); /* _reg_SLCDC_CBUF_SIZE */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x10)); /* _reg_SLCDC_CBUF_SSIZE */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x14)); /* _reg_SLCDC_FIFO_CONFIG */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x18)); /* _reg_SLCDC_LCD_CONFIG */
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x1C));
+ /*_reg_SLCDC_LCD_TXCONFIG*/
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ __raw_writel(0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x24));
+ /*_reg_SLCDC_LCD_CLKCONFIG*/
+}
+
+/**
+ *@brief slcdc initial configuration
+ *
+ * Function Name: slcdc_config
+ *
+ * Description:This routine will fist time configuration like buffer address
+ * FIFO configuration etc.
+ *
+ **/
+void slcdc_config(int datlen)
+{
+ u32 xfrmode, sckpol, worddefcom, imgend = 0, worddefwrite =
+ 0, worddefdat = 0;
+ volatile unsigned long reg;
+
+ if (datlen == 8) {
+ imgend = 0x2; /*8-bit little endian; */
+ worddefdat = 0; /* 8-bit data */
+ worddefwrite = 10;
+ } else if (datlen == 16) {
+ imgend = 0x1; /* 16-bit little endian; */
+ worddefdat = 1; /* 16-bit data */
+ worddefwrite = 1;
+ } else {
+ FAILED(":invaild parameter, 8 or 16 is the value required");
+ }
+ worddefcom = 1;
+#ifdef SLCDC_SERIAL_MODE
+ xfrmode = 0; /* serial mode */
+#else
+ xfrmode = 1; /* paralle mode */
+#endif
+ sckpol = 1; /* falling edge */
+ /* config to be little endian serial 16bit */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x1C));
+ /*_reg_SLCDC_LCD_TXCONFIG*/
+ reg =
+ (imgend << 16) | (worddefdat << 4) | (worddefcom << 3) | (xfrmode <<
+ 2) |
+ (sckpol);
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x1C));
+
+ /* printk("SLCDC_TXCONFIG = %x \n",_reg_SLCDC_LCD_TXCONFIG); */
+ /* config dma setting */
+ __raw_writel(5, IO_ADDRESS(SLCDC_BASE_ADDR + 0x14)); /* _reg_SLCDC_FIFO_CONFIG,
+ burst length is 4 32-bit words */
+ /* config buffer address setting */
+ __raw_writel((u32) (slcdc_par.screen_start_address), IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+ __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x08)); /* _reg_SLCDC_CBADDR */
+
+ /* config clk setting */
+ __raw_writel((u32) 0x3, IO_ADDRESS(SLCDC_BASE_ADDR + 0x24));
+ /*_reg_SLCDC_LCD_CLKCONFIG*/
+ /* set GO 0 */
+ __raw_writel((u32) 0x0, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+
+ slcdc_delay(5000);
+
+}
+
+/**
+ *@brief slcdc send command routine
+ *
+ * Function Name: slcdc_send_cmd
+ *
+ * Description:This help routine sends command to the SLCD from SLCDC
+ *
+ *@return 0 on success, any other value otherwise
+ **/
+/* for command transfer, it is very short, will not use interrupt */
+int slcdc_send_cmd(u32 length)
+{
+ u32 status;
+ volatile unsigned long reg;
+
+ /* disable interrupt */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg = reg & 0xffffff7f;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ /* set length */
+ __raw_writel(length, IO_ADDRESS(SLCDC_BASE_ADDR + 0x04)); /* _reg_SLCDC_DBUF_SIZE */
+
+ /* set automode 00 for command */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ reg = reg & 0x000001ff;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ /* set GO */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ reg |= 0x1;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ /* polling for data transfer finish */
+
+ status = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ while ((!(status & SLCDC_TRANSFER_ERROR))
+ && (status & SLCDC_TRANSFER_BUSY)) {
+ status = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ }
+
+ if (status & SLCDC_TRANSFER_ERROR) {
+ TRACE("send cmd error status=0x%x \n", status);
+ return 1;
+ } else {
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ reg |= 0x40;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20)); /* _reg_SLCDC_LCD_CTRL_STAT */
+ reg |= 0x20;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ return 0;
+ }
+}
+
+/**
+ *@brief slcdc send data routine
+ *
+ * Function Name: slcdc_send_data
+ *
+ * Description: This help routine sends data to the SLCD from SLCDC
+ *
+ *@return 0 on success, any other value otherwise
+ **/
+void slcdc_send_data(u32 length)
+{
+ volatile unsigned long reg;
+
+ /* enable interrupt */
+#ifdef USING_INTERRUPT_SLCDC
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= ~0xffffff7f;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+#endif
+ /* set length */
+ __raw_writel(length, IO_ADDRESS(SLCDC_BASE_ADDR + 0x04)); /* _reg_SLCDC_DBUF_SIZE */
+ /* set automode 01 for data */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x00000800;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ /* set GO */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x1;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+#ifdef USING_INTERRUPT_SLCDC
+ interruptible_sleep_on(&slcdc_wait);
+#else
+ do {
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ } while ((reg & 0x00000004) != 0);
+
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x40;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x20;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+#endif
+ return;
+
+}
+
+/**
+ *@brief slcdc isr
+ *
+ * Function Name: slcdc_isr
+ *
+ * Description: This ISR routine takes interrupt from SLCDC and does refresh of
+ * display data if necessary
+ *
+ *@return 0 on success, any other value otherwise
+ **/
+static irqreturn_t slcdc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ volatile u32 reg;
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+
+ /* clear interrupt */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x40;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ wake_up_interruptible(&slcdc_wait);
+
+ if (start_fb_timer_flag == 1) {
+ /* while((_reg_SLCDC_LCD_CTRL_STAT &0x00000004)!=0); */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x40;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x20;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ mod_timer(&slcdc_timer, jiffies + SLCDC_REFRESH_RATE);
+ TRACE("slcdc_isr\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ *@brief slcdc buffer initialization
+ *
+ * Function Name: slcdc_init_buffer
+ *
+ *
+ * Description:This routine will allocate physical memory for SLCDC data and
+ * for SLCDC command.
+ *
+ *@return 0 on success, appropriate error value on error
+ **/
+int slcdc_init_buffer(void)
+{
+
+ TRACE("slcdc data buffer size = %x \n",
+ (unsigned int)SLCDC_DATA_MEM_SIZE);
+
+ if (g_slcdc_dbuffer_phyaddress != NULL)
+ return -EINVAL;
+
+ if (g_slcdc_cbuffer_phyaddress != NULL)
+ return -EINVAL;
+
+ g_slcdc_dbuffer_phyaddress = (u16 *) mxc_malloc(SLCDC_DATA_MEM_SIZE);
+ g_slcdc_cbuffer_phyaddress = (u16 *) mxc_malloc(SLCDC_CMD_MEM_SIZE);
+
+ if (!g_slcdc_dbuffer_phyaddress || !g_slcdc_cbuffer_phyaddress) {
+ if (g_slcdc_dbuffer_phyaddress != (u16 *) 0)
+ mxc_free((u32) g_slcdc_dbuffer_phyaddress);
+
+ if (g_slcdc_cbuffer_phyaddress != (u16 *) 0)
+ mxc_free((u32) g_slcdc_cbuffer_phyaddress);
+
+ FAILED("can not allocated memory\n");
+ return -ENOMEM;
+ } else {
+
+ TRACE
+ ("allocated cmd_buffer=0x%x size=%d, data_buffer=0x%x size=%d",
+ (int)g_slcdc_cbuffer_phyaddress, SLCDC_CMD_MEM_SIZE,
+ (int)g_slcdc_dbuffer_phyaddress, SLCDC_DATA_MEM_SIZE);
+
+ if ((!request_mem_region((u32) g_slcdc_dbuffer_phyaddress,
+ SLCDC_DATA_MEM_SIZE,
+ slcdc_fb_info.fix.id))
+ ||
+ (!request_mem_region
+ ((u32) g_slcdc_cbuffer_phyaddress, SLCDC_CMD_MEM_SIZE,
+ slcdc_fb_info.fix.id))) {
+ FAILED("request mem region failed.");
+ return -EBUSY;
+ }
+
+ if (!(slcdc_fb_info.screen_base = ioremap((u32)
+ g_slcdc_dbuffer_phyaddress,
+ SLCDC_DATA_MEM_SIZE)))
+ {
+ release_mem_region((u32) g_slcdc_dbuffer_phyaddress,
+ SLCDC_DATA_MEM_SIZE);
+ FAILED("Unable to map fb memory to virtual address");
+ return -EIO;
+ } else {
+ g_slcdc_dbuffer_address =
+ (u16 *) slcdc_fb_info.screen_base;
+ }
+
+ if (!
+ (g_slcdc_cbuffer_address =
+ ioremap((u32) g_slcdc_cbuffer_phyaddress,
+ SLCDC_CMD_MEM_SIZE))) {
+ release_mem_region((u32) g_slcdc_dbuffer_phyaddress,
+ SLCDC_DATA_MEM_SIZE);
+ release_mem_region((u32) g_slcdc_cbuffer_phyaddress,
+ SLCDC_CMD_MEM_SIZE);
+ FAILED("Unable to map fb memory to virtual address");
+ return -EIO;
+ }
+ }
+
+ TRACE("slcdc data buffer address = %x cmd buffer address= %x \n",
+ (unsigned int)slcdc_par.screen_start_address,
+ (unsigned int)g_slcdc_cbuffer_address);
+
+ return 0;
+}
+
+/**
+ *@brief slcdc buffer de initialization
+ *
+ * Function Name: slcdc_free_buffer
+ *
+ *
+ * Description:This routine will deallocate the physical memory allocated by
+ * slcdc_init_buffer.
+ *
+ *@return 0 on success
+ **/
+int slcdc_free_buffer(void)
+{
+
+ FUNC_START;
+
+ iounmap(g_slcdc_dbuffer_address);
+ iounmap(g_slcdc_cbuffer_address);
+
+ release_mem_region((unsigned long)g_slcdc_dbuffer_phyaddress,
+ SLCDC_DATA_MEM_SIZE);
+ release_mem_region((unsigned long)g_slcdc_cbuffer_phyaddress,
+ SLCDC_CMD_MEM_SIZE);
+
+ mxc_free((u32) g_slcdc_dbuffer_phyaddress);
+ mxc_free((u32) g_slcdc_cbuffer_phyaddress);
+
+ FUNC_END;
+ return 0;
+}
+
+/**
+ *@brief slcdc mmap function
+ *
+ * Function Name: slcdc_mmap
+ *
+ *
+ * Description: This is the memory map routine for this driver,will setup the
+ * memory map used in this driver.
+ *
+ *@param filp the pointer to the file descripter
+ *@param vma the pointer to the vma structure related to the driver
+ *
+ *@return int return status
+ * @li 0 sucessful
+ * @li other failed
+ * Modification History:
+ * Dec,2003, Karen first version
+ *
+ **/
+
+static int slcdc_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ unsigned long page, pos;
+ unsigned long start = (unsigned long)vma->vm_start;
+ unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
+
+ if (size > SLCDC_DATA_MEM_SIZE)
+ return -EINVAL;
+
+ TRACE("slcdc_mmap is called 1\n");
+ pos = (unsigned long)slcdc_par.v_screen_start_address;
+
+ while (size > 0) {
+ page = virt_to_phys((void *)pos);
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ /* This is an IO map - tell maydump to skip this VMA */
+ vma->vm_flags |= VM_IO;
+
+ if (remap_pfn_range
+ (vma, start, page >> PAGE_SHIFT, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+/**
+ *@brief slcdc mmap function called from framebuffer
+ *
+ * Function Name: slcdcfb_mmap
+ *
+ *
+ * Description: This is the memory map routine for this driver,will setup the
+ * memory map used in this driver.
+ *
+ *@return 0 on success any other value for failure
+ **/
+static int slcdcfb_mmap(struct fb_info *info, struct file *file,
+ struct vm_area_struct *vma)
+{
+
+ return slcdc_mmap(file, vma);
+
+}
+
+/**
+ *@brief slcdc display-on function
+ *
+ * Function Name: slcdc_display_on
+ *
+ * Description:This helper routine will send the command to the SLCD for display
+ * on.
+ *
+ **/
+void slcdc_display_on(void)
+{
+ u16 *databuffer = NULL;
+
+ __raw_writel((u32) g_slcdc_cbuffer_phyaddress,
+ IO_ADDRESS(SLCDC_BASE_ADDR + 0x00));
+
+ /* put cmd into cmd buffer */
+ databuffer = g_slcdc_cbuffer_address;
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_DISON;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ /* delay */
+ slcdc_delay(0xffff);
+ /* send cmd */
+ slcdc_send_cmd(1);
+ /* delay */
+ slcdc_delay(0xffff);
+}
+
+/**
+ *@brief slcdc display-off function
+ *
+ * Function Name: slcdc_display_off
+ *
+ * Description:This helper routine will send the command to the SLCD for display
+ * off.
+ *
+ **/
+void slcdc_display_off(void)
+{
+ u16 *databuffer = NULL;
+ /* Cause LCD module to enter sleep mode.Bu sure to input display OFF command
+ to turn off the display before inputting the SLPIN command
+ Keep the logic power supply turned on for 40ms after the LCD module
+ enters the sleep mode. */
+ /* put cmd into cmd buffer */
+ databuffer = slcdc_par.v_screen_start_address;
+ /* send cmd SLPIN */
+ *databuffer = SLCDC_CMD_SLPIN;
+ slcdc_send_cmd(1);
+ /* put reset high */
+
+#if 0
+#ifdef SLCDC_MUX_SSI3_SLCDC2
+ /* set reset pin to high */
+ _reg_GPIO_DR(GPIOA) |= 0x80000000;
+#endif
+#ifdef SLCDC_MUX_SD_SLCDC1
+ /* set reset pin to high */
+ _reg_GPIO_DR(GPIOB) |= 0x00000020;
+#endif
+#endif
+
+ /* delay for 50ms */
+ slcdc_delay(0xffff);
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_DISOFF;
+ /* send cmd DISOFF */
+ slcdc_send_cmd(1);
+ TRACE("disoff \n");
+ slcdc_delay(0xffff);
+}
+
+/**
+ *@brief slcdc display-off function
+ *
+ * Function Name: slcdc_display_normal
+ *
+ * Description:This helper routine will send the command to the SLCD for display
+ * normal.
+ *
+ **/
+void slcdc_display_normal(void)
+{
+ u16 *databuffer = NULL;
+ databuffer = slcdc_par.v_screen_start_address;
+ *databuffer = SLCDC_CMD_DISNOR;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ /* delay */
+ slcdc_delay(100);
+}
+
+/**
+ *@brief slcdc display sleep out function
+ *
+ * Function Name: slcdc_sleep_out
+ *
+ * Description:This helper routine will send the command to the SLCD for wakeup
+ * from sleep.
+ *
+ **/
+void slcdc_sleep_out(void)
+{
+ u16 *databuffer = NULL;
+ databuffer = slcdc_par.v_screen_start_address;
+ *databuffer = SLCDC_CMD_SLPOUT;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ /* delay */
+ slcdc_delay(0xffff);
+}
+
+/** @brief sends control command to SLCD */
+void slcdc_display_ctl(void)
+{
+ int i;
+#ifdef SLCDC_SERIAL_MODE
+ u16 disctl[11] = { 0x1c00, 0x0200, 0x8200, 0x0000,
+ 0x1e00, 0xe000, 0x0000, 0xdc00,
+ 0x0000, 0x0200, 0x0000
+ };
+#else
+ u16 disctl[11] = { 0x1c, 0x02, 0x82, 0x00,
+ 0x1e, 0xe0, 0x00, 0xdc,
+ 0x00, 0x02, 0x00
+ };
+#endif
+ u16 *databuffer = NULL;
+ /* It make various display timing settings. */
+ databuffer = slcdc_par.v_screen_start_address;
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_DISCTL;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(2000);
+ /* put parameter into data buffer */
+ for (i = 0; i < 11; i++) {
+ *databuffer = disctl[i];
+ databuffer++;
+ }
+ /* send data */
+ slcdc_send_data(11);
+
+}
+
+/**
+ *@brief slcd page set function
+ *
+ * Function Name: slcdc_page_set
+ *
+ * Description:This helper routine will send the command to the SLCD to set the
+ * page inside video ram
+ *
+ **/
+void slcdc_page_set(void)
+{
+ /* It specify a page address area in order to access the display data
+ RAM from the MPU. Be sure to set both starting and ending pages. */
+ int i;
+ u16 *databuffer = NULL;
+#ifdef SLCDC_SERIAL_MODE
+ u16 pset[4] = { 0x0000, 0x0000, 0xb100, 0x0000 };
+#else
+ u16 pset[4] = { 0x00, 0x00, 0xb1, 0x00 };
+#endif
+ databuffer = (u16 *) (slcdc_par.v_screen_start_address);
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_SD_PSET;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(20000);
+
+ /* put parameter into data buffer */
+ for (i = 0; i < 4; i++) {
+ *databuffer = pset[i];
+ databuffer++;
+ }
+ /* send data */
+ slcdc_send_data(4);
+
+ slcdc_delay(20000);
+}
+
+/**
+ *@brief slcd column set function
+ *
+ * Function Name: slcdc_col_set
+ *
+ * Description:This helper routine will send the command to the SLCD to set the
+ * column inside video ram
+ *
+ **/
+void slcdc_col_set(void)
+{
+ /* It specify starting and ending collumns. */
+ int i;
+#ifdef SLCDC_SERIAL_MODE
+ u16 cset[4] = { 0x0200, 0x0000, 0xb100, 0x0000 };
+#else
+ u16 cset[4] = { 0x02, 0x00, 0xb1, 0x00 };
+#endif
+ u16 *databuffer = NULL;
+ databuffer = (u16 *) (slcdc_par.v_screen_start_address);
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_SD_CSET;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(20000);
+ /* put parameter into data buffer */
+ for (i = 0; i < 4; i++) {
+ *databuffer = cset[i];
+ databuffer++;
+ }
+ /* send data */
+ slcdc_send_data(4);
+ slcdc_delay(20000);
+
+}
+
+void slcdc_data_ctl(void)
+{
+ u8 *databuffer = NULL;
+ databuffer = (u8 *) (slcdc_par.v_screen_start_address);
+ *databuffer = SLCDC_CMD_DATCTL;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(20000);
+ *databuffer = 0x28;
+ /* send data */
+ slcdc_send_data(1);
+ slcdc_delay(20000);
+}
+
+void slcdc_volctl(void)
+{
+ u16 *databuffer = NULL;
+ databuffer = slcdc_par.v_screen_start_address;
+ *databuffer = SLCDC_CMD_VOLCTR;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(20000);
+#ifdef SLCDC_SERIAL_MODE
+ *databuffer = 0x9f00;
+#else
+ *databuffer = 0x9f;
+#endif
+ /* send data */
+ slcdc_send_data(1);
+ slcdc_delay(20000);
+}
+
+void slcdc_ascset(void)
+{
+ int i;
+#ifdef SLCDC_SERIAL_MODE
+ u16 lcd_para_ASCSET[7] = { 0x0000, 0x0000, 0x1f00, 0x0100,
+ 0x1f00, 0x0100, 0x0300
+ };
+#else
+ u16 lcd_para_ASCSET[7] = { 0x00, 0x00, 0x1f, 0x01,
+ 0x1f, 0x01, 0x03
+ };
+
+#endif
+ u16 *databuffer = NULL;
+ databuffer = (u16 *) (slcdc_par.v_screen_start_address);
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_ASCSET;
+ /* put parameter into data buffer */
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(2000);
+ for (i = 0; i < 7; i++) {
+ *databuffer = lcd_para_ASCSET[i];
+ databuffer++;
+ }
+ /* send data */
+ slcdc_send_data(7);
+ slcdc_delay(20000);
+}
+
+void slcdc_scstart(void)
+{
+ int i;
+ u16 lcd_para_SCSTART[2] = { 0x00, 0x00 };
+ u16 *databuffer = NULL;
+ databuffer = (u16 *) (slcdc_par.v_screen_start_address);
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_SCSTART;
+ /* put parameter into data buffer */
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(10000);
+ for (i = 0; i < 2; i++) {
+ *databuffer = lcd_para_SCSTART[i];
+ databuffer++;
+ }
+ /* send data */
+ slcdc_send_data(2);
+ slcdc_delay(20000);
+
+}
+
+void slcdc_config_panel(void)
+{
+ int i;
+#if 1
+ /* set data format */
+ slcdc_config(8);
+ slcdc_delay(0xffff);
+ slcdc_reset(1); /* pull reset signal high */
+
+ /* set data format */
+ slcdc_delay(20000);
+ slcdc_data_ctl();
+ slcdc_delay(1000);
+ slcdc_config(16);
+
+ /* sleep out */
+ slcdc_sleep_out();
+ slcdc_delay(20000);
+
+ for (i = 0; i < 8; i++) {
+ slcdc_volctl();
+ slcdc_delay(2000);
+ }
+ slcdc_delay(0xffff);
+ slcdc_delay(0xffff);
+ slcdc_delay(0xffff);
+
+ slcdc_display_on();
+ slcdc_delay(0xffff);
+ /* set col address */
+ slcdc_col_set();
+ /* set page address */
+ slcdc_page_set();
+ /* set area in screen to be used for scrolling */
+ slcdc_ascset();
+ slcdc_delay(20000);
+ /* set top scroll page within the scroll area */
+ slcdc_scstart();
+ mdelay(4);
+#endif
+#if 0
+
+ //set data format
+ slcdc_config(8);
+ slcdc_delay(0xffff);
+ slcdc_reset(1); //pull reset signal high
+
+ //set data format
+ slcdc_delay(20000);
+ slcdc_data_ctl();
+ slcdc_delay(1000);
+ slcdc_config(16);
+ //sleep out
+ slcdc_sleep_out();
+ slcdc_delay(0xffff);
+ //set col address
+ slcdc_col_set();
+ //set page address
+ slcdc_page_set();
+ //set area in screen to be used for scrolling
+ slcdc_ascset();
+ slcdc_delay(20000);
+ //set top scroll page within the scroll area
+ slcdc_scstart();
+ slcdc_delay(0xffff);
+ //turn on slcd display
+ slcdc_display_on();
+ slcdc_delay(0xffff);
+ slcdc_delay(0xffff);
+
+ for (i = 0; i < 8; i++) {
+ slcdc_volctl();
+ slcdc_delay(2000);
+ }
+#endif
+}
+
+/**
+ *@brief slcdc ioctl routine
+ *
+ * Function Name: slcdc_ioctl
+ * Description:This routine will implement driver-specific functions
+ *
+ *@param inode : the pointer to driver-related inode.
+ *@param filp : the pointer to driver-related file structure.
+ *@param cmd : the command number.
+ *@param arg: argument which depends on command.
+ *
+ *@return int return status
+ * @li 0 sucess
+ * @li 1 failure
+ *
+ * Modification History:
+ * Dec,2003 Karen first version for MX21 TO2
+ *
+ **/
+
+static int slcdc_ioctl(struct inode *inode, struct file *filp,
+ u_int cmd, u_long arg)
+{
+ u16 *databuffer = NULL;
+
+ switch (cmd) {
+ case SLCDC_CMD_DISON:
+ slcdc_display_on();
+ break;
+
+ case SLCDC_CMD_DISOFF:
+ slcdc_display_off();
+ break;
+
+ case SLCDC_CMD_DISNOR:
+ slcdc_display_normal();
+ break;
+
+ case SLCDC_CMD_DISINV:
+ /* put cmd into cmd buffer */
+ databuffer = slcdc_par.v_screen_start_address;
+ *databuffer = SLCDC_CMD_DISINV;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(100);
+ break;
+
+ case SLCDC_CMD_DATCTL:
+ break;
+
+ case SLCDC_CMD_RAMWR:
+ /* Causes the MPU to be a data entry mode,allowing it to serite data
+ in the display memory. Inputting any other cmds other than NOP
+ cancels the data entry mode. */
+
+ __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+
+ /* put cmd into cmd buffer */
+ databuffer = g_slcdc_cbuffer_address;
+ /* put cmd into cmd buffer */
+ *databuffer = SLCDC_CMD_RAMWR;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(2000);
+
+ /* this is to display one data per time, it is ok. */
+ slcdc_delay(0xffff);
+ __raw_writel((u32) slcdc_par.screen_start_address, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+
+ slcdc_delay(0xffff);
+ slcdc_send_data(SLCDC_MEM_SIZE);
+ slcdc_delay(0xffff);
+
+ break;
+
+ case SLCDC_CMD_RAMRD:
+ break;
+
+ case SLCDC_CMD_PTLIN:
+ /* This command is used to display a partial screen for power saving. */
+ break;
+
+ case SLCDC_CMD_PTLOUT:
+ /* This command is used to exit the partila diaplay mode. */
+ break;
+
+ case SLCDC_CMD_GCP64:
+ /* make 63 pulse position settings of GCP for 64 gray scales. */
+ /* send cmd into cmd buffer
+ send parameter into data buffer
+ send cmd
+ send data */
+ break;
+
+ case SLCDC_CMD_GCP16:
+ break;
+
+ case SLCDC_CMD_GSSET:
+ break;
+
+ case SLCDC_CMD_ASCSET:
+ /* make partial screen scroll settings. */
+ /* send cmd into cmd buffer
+ send parameter into data buffer
+ send cmd
+ delay
+ send data
+ delay */
+ break;
+
+ case SLCDC_CMD_SCSTART:
+ /* set a scroll starting page in the scrolling area.Be sure to send
+ this cmd after ASCSET . */
+ /* send cmd into cmd buffer
+ send parameter into data buffer
+ send cmd
+ delay
+ send data
+ delay */
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int slcdcfb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg,
+ struct fb_info *info)
+{
+ return slcdc_ioctl(inode, file, cmd, arg);
+}
+
+/**
+ *@brief slcdc close function
+ *
+ * Function Name: slcdc_release
+ *
+ *
+ * Description: This is the release routine for the driver. And this function \n
+ * will be called while the module being closed. In this function, it will\n
+ * unregister the apm
+ *
+ *@param inode the pointer to the inode descripter
+ *@param filp the pointer to the file descripter
+ *
+ *@return int return status
+ * @li 0 sucessful
+ * @li other failed
+ * Modification History:
+ * Dec,2003 Karen first version
+ *
+ **/
+
+int slcdc_release(struct inode *inode, struct file *filp)
+{
+ TRACE("slcdc_release: ----\n");
+ del_timer_sync(&slcdc_timer);
+ return 0;
+}
+
+/**
+ *@brief slcdc timer call back function
+ *
+ * Function Name: slcdc_timer_func
+ *
+ * Description:This helper routine prepare the SLCDC for data transfer
+ *
+ **/
+static void slcdc_timer_func(unsigned long args)
+{
+ volatile unsigned long reg;
+ /* Causes the MPU to be a data entry mode,allowing it to serite data in the
+ display memory. Inputting any other cmds other than NOP cancels the data
+ entry mode. */
+
+ __raw_writel((u32) g_slcdc_cbuffer_phyaddress, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+
+ /* put cmd into cmd buffer */
+ *g_slcdc_cbuffer_address = SLCDC_CMD_RAMWR;
+ /* send cmd */
+ slcdc_send_cmd(1);
+ slcdc_delay(2000);
+
+ /* this is to display one data per time, it is ok. */
+ slcdc_delay(0xffff);
+ __raw_writel((u32) slcdc_par.screen_start_address, IO_ADDRESS(SLCDC_BASE_ADDR + 0x00)); /* _reg_SLCDC_DBADDR */
+
+ slcdc_delay(0xffff);
+ /* slcdc_send_data( SLCDC_MEM_SIZE); */
+
+ /* enable interrupt */
+#ifdef USING_INTERRUPT_SLCDC
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= ~0xffffff7f;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+#endif
+ /* set length */
+ __raw_writel(SLCDC_MEM_SIZE, IO_ADDRESS(SLCDC_BASE_ADDR + 0x04)); /* _reg_SLCDC_DBUF_SIZE */
+ /* set automode 01 for data */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+ reg |= 0x00000800;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+
+ /* set GO */
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ /*_reg_SLCDC_LCD_CTRL_STAT*/
+
+ reg |= 0x1;
+ __raw_writel(reg, IO_ADDRESS(SLCDC_BASE_ADDR + 0x20));
+ slcdc_delay(0xffff);
+
+ return;
+}
+
+/**
+ *@brief slcdc timer initialization
+ *
+ * Function Name: init_slcdc_timer
+ *
+ * Description:This helper routine prepare the SLCDC timer for refreshing the
+ * screen
+ *
+ **/
+static void init_slcdc_timer(void)
+{
+
+ init_timer(&slcdc_timer);
+ slcdc_timer.expires = jiffies + SLCDC_REFRESH_RATE;
+ slcdc_timer.data = 0;
+ slcdc_timer.function = slcdc_timer_func;
+ add_timer(&slcdc_timer);
+ start_fb_timer_flag = 1;
+}
+
+/**
+ *@brief slcdc open function
+ *
+ * Function Name: slcdc_open
+ *
+ *
+ * Description: This is the open routine for the driver. And this function \n
+ * will be called while the module being opened. In this function, it will\n
+ * @li configure GPIO for serial/parallel
+ * @li slcdc reset
+ * @li init slcd registers
+ * @li init waitqueue
+ * @li get rca, select the card
+ * @li send some command for panel configuration
+ *
+ *@param inode the pointer to the inode descripter
+ *@param filp the pointer to the file descripter
+ *
+ *@return int return status
+ * @li 0 sucessful
+ * @li other failed
+ * Modification History:
+ * Dec,2003, Karen first version
+ * June,2004, Shirley update for Parallel mode
+ *
+ **/
+
+int slcdc_open(struct inode *inode, struct file *filp)
+{
+ volatile unsigned long reg;
+
+ TRACE("slcdc_open: ----\n");
+
+ /* init dev */
+#ifdef SLCDC_SERIAL_MODE
+ slcdc_gpio_serial();
+#else
+ slcdc_gpio_paralle();
+#endif
+
+ slcdc_init_dev();
+ slcdc_reset(0); /*pull reset low */
+ /* init slcd registers */
+ slcdc_init_reg();
+
+ /* init waitqueue */
+ init_waitqueue_head(&slcdc_wait);
+ /* send some command for panel configuration */
+ slcdc_config_panel();
+ reg = __raw_readl(IO_ADDRESS(SLCDC_BASE_ADDR + 0x1C));
+ /*_reg_SLCDC_LCD_TXCONFIG*/
+ TRACE("TRANS_CONFIG_REG=%x \n", (unsigned int)reg);
+
+ /* init slcdc timer, and start timer */
+ init_slcdc_timer();
+ return 0;
+}
+
+/**
+ *@brief slcdc init function
+ *
+ * Function Name: slcdc_init
+ *
+ *
+ *@return int return status
+ * @li 0 sucess
+ * @li other failure
+ *
+ * Description: This is the initialization routine for the driver. And this
+ * function \n will be called while the module being installed.
+ * In this function, it will \n register char device,request
+ * slcdc irq, initialize the buffer,register to\npower management.
+ *
+ * Modification History:
+ * Dec,2003, Karen update for MX21 TO2
+ *
+ **/
+
+int __init slcdc_init(void)
+{
+ int tmp, err;
+
+ INFO("SLCDC Driver \n");
+ INFO("Motorola SPS-SuZhou \n");
+
+ _init_fbinfo();
+
+ if (slcdcfb_set_var(&slcdc_fb_info)) ; /* current_par.allow_modeset = 0; */
+
+ register_framebuffer(&slcdc_fb_info);
+
+ devfs_mk_cdev(slcdc_device_num, S_IFCHR | S_IRUGO | S_IWUSR, "slcdc");
+
+ if (slcdc_major != 0) {
+ /* use user supplied major number */
+ slcdc_device_num = MKDEV(slcdc_major, slcdc_minor);
+
+ err = register_chrdev_region(slcdc_device_num, 1, MODULE_NAME);
+ } else {
+ /* auto create device major number */
+ err =
+ alloc_chrdev_region(&slcdc_device_num, slcdc_minor, 1,
+ MODULE_NAME);
+ }
+
+ if (err < 0) {
+ TRACE("%s driver: Unable to register chrdev region\n",
+ MODULE_NAME);
+ return err;
+ }
+
+ cdev_init(&slcdc_dev, &g_slcdc_fops);
+ slcdc_dev.owner = THIS_MODULE;
+ slcdc_dev.ops = &g_slcdc_fops;
+
+ if ((err = cdev_add(&slcdc_dev, slcdc_device_num, 1))) {
+ TRACE
+ ("%s driver: Unable to create character device. Error code=%d\n",
+ MODULE_NAME, err);
+ return -ENODEV;
+ }
+
+ /* init interrupt */
+ tmp = request_irq(SLCDC_IRQ,
+ (void *)slcdc_isr,
+ SA_INTERRUPT | SA_SHIRQ, MODULE_NAME, MODULE_NAME);
+ if (tmp) {
+ printk("slcdc_init:cannot init major= %d irq=%d\n",
+ MAJOR(slcdc_device_num), SLCDC_IRQ);
+ devfs_remove(MODULE_NAME);
+ cdev_del(&slcdc_dev);
+ return -1;
+ }
+
+ /* init buffer */
+ /* initialize buffer address */
+ g_slcdc_dbuffer_address = NULL;
+ g_slcdc_dbuffer_phyaddress = NULL;
+ g_slcdc_cbuffer_address = NULL;
+ g_slcdc_cbuffer_phyaddress = NULL;
+ slcdc_init_buffer();
+
+ g_slcdc_status = 0;
+
+ return 0;
+}
+
+/**
+ *@brief slcdc cleanup function
+ *
+ * Function Name: slcdc_cleanup
+ *
+ *@return None
+ *
+ * Description: This is the cleanup routine for the driver. And this function \n
+ * will be called while the module being removed. In this function, it will \n
+ * cleanup all the registered entries
+ *
+ * Modification History:
+ * Dec 2003, Karen update for MX21 TO2
+ *
+ **/
+
+void __exit slcdc_cleanup(void)
+{
+
+ unregister_chrdev_region(slcdc_device_num, 1);
+
+ devfs_remove(MODULE_NAME);
+
+ /*Do some cleanup work */
+ free_irq(SLCDC_IRQ, MODULE_NAME);
+
+ unregister_framebuffer(&slcdc_fb_info);
+
+ slcdc_free_buffer();
+
+ cdev_del(&slcdc_dev);
+
+ return;
+}
+
+module_init(slcdc_init);
+module_exit(slcdc_cleanup);
+
+/*////////////// Frame Buffer Suport /////////////////////////////////// */
+
+/**
+ * _check_var - Validates a var passed in.
+ * @var: frame buffer variable screen structure
+ * @info: frame buffer structure that represents a single frame buffer
+ *
+ * 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.
+ *
+ * Returns negative errno on error, or zero on success.
+ */
+static int _check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ const struct slcdcfb_par *par = (const struct slcdcfb_par *)info->par;
+
+ if (var->xres > current_par.max_xres)
+ var->xres = current_par.max_xres;
+ if (var->yres > current_par.max_yres)
+ var->yres = current_par.max_yres;
+
+ var->xres_virtual = var->xres_virtual < par->xres
+ ? par->xres : var->xres_virtual;
+ var->yres_virtual = var->yres_virtual < par->yres
+ ? par->yres : var->yres_virtual;
+ var->bits_per_pixel = par->bits_per_pixel;
+
+ switch (var->bits_per_pixel) {
+ case 2:
+ case 4:
+ case 8:
+ var->red.length = 4;
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.length = 0;
+ break;
+
+ case 12: /* RGB 444 */
+ case 16: /* RGB 565 */
+ TRACE("16->a\n");
+ var->red.length = 4;
+ var->blue.length = 4;
+ var->green.length = 4;
+ var->transp.length = 0;
+#ifdef __LITTLE_ENDIAN
+ TRACE("16->b\n");
+ var->red.offset = 8;
+ var->green.offset = 4;
+ var->blue.offset = 0;
+ var->transp.offset = 0;
+#endif /* __LITTLE_ENDIAN */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* *var->screen_start_address=(u_char*)((u_long)g_slcdc_dbuffer_phyaddress );
+ *var->v_screen_start_address=(u_char*)((u_long)g_slcdc_dbuffer_address ); */
+
+ 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;
+}
+
+/**
+ *@brief Use current_par to set a var structure
+ *
+ *@param var Input var data
+ *@param par LCD controller parameters
+ *
+ *@return If no error, return 0
+ *
+ */
+static int _encode_var(struct fb_var_screeninfo *var, struct slcdcfb_par *par)
+{
+ FUNC_START
+ /* Don't know if really want to zero var on entry.
+ Look at set_var to see. If so, may need to add extra params to par */
+ memset(var, 0, sizeof(*var));
+ var->xres = par->xres;
+ TRACE("var->xress=%d\n", var->xres);
+ var->yres = par->yres;
+ TRACE("var->yres=%d\n", var->yres);
+ var->xres_virtual = par->xres_virtual;
+ TRACE("var->xres_virtual=%d\n", var->xres_virtual);
+ var->yres_virtual = par->yres_virtual;
+ TRACE("var->yres_virtual=%d\n", var->yres_virtual);
+
+ var->bits_per_pixel = par->bits_per_pixel;
+ TRACE("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+
+ switch (var->bits_per_pixel) {
+ case 2:
+ case 4:
+ case 8:
+ var->red.length = 4;
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.length = 0;
+ break;
+ case 12: /* This case should differ for Active/Passive mode */
+ case 16:
+ TRACE("16->a\n");
+ var->red.length = 4;
+ var->blue.length = 4;
+ var->green.length = 4;
+ var->transp.length = 0;
+#ifdef __LITTLE_ENDIAN
+ TRACE("16->b\n");
+ var->red.offset = 8;
+ var->green.offset = 4;
+ var->blue.offset = 0;
+ var->transp.offset = 0;
+#endif /* __LITTLE_ENDIAN */
+ break;
+ }
+
+ FUNC_END return 0;
+}
+
+/**
+ *@brief Get the video params out of 'var'. If a value doesn't fit,
+ * round it up,if it's too big, return -EINVAL.
+ *
+ *@warning Round up in the following order: bits_per_pixel, xres,
+ * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
+ * bitfields, horizontal timing, vertical timing.
+ *
+ *@param var Input var data
+ *@param par LCD controller parameters
+ *
+ *@return If no error, return 0
+ */
+static int _decode_var(struct fb_info *info)
+{
+ struct fb_var_screeninfo *var = &info->var;
+ struct slcdcfb_par par_var;
+ struct slcdcfb_par *par = &par_var;
+
+ FUNC_START *par = current_par;
+
+ if ((par->xres = var->xres) < MIN_XRES)
+ par->xres = MIN_XRES;
+ if ((par->yres = var->yres) < MIN_YRES)
+ par->yres = MIN_YRES;
+ if (par->xres > current_par.max_xres)
+ par->xres = current_par.max_xres;
+ if (par->yres > current_par.max_yres)
+ par->yres = current_par.max_yres;
+ par->xres_virtual = var->xres_virtual < par->xres
+ ? par->xres : var->xres_virtual;
+ par->yres_virtual = var->yres_virtual < par->yres
+ ? par->yres : var->yres_virtual;
+ par->bits_per_pixel = var->bits_per_pixel;
+
+ switch (par->bits_per_pixel) {
+
+ case 4:
+ par->visual = FB_VISUAL_PSEUDOCOLOR;
+ par->palette_size = 16;
+ break;
+
+ case 8:
+ par->visual = FB_VISUAL_PSEUDOCOLOR;
+ par->palette_size = 256;
+ break;
+
+ case 12: /* RGB 444 */
+ case 16: /* RGB 565 */
+ par->visual = FB_VISUAL_TRUECOLOR;
+ par->palette_size = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ par->screen_start_address =
+ (u_char *) ((u_long) g_slcdc_dbuffer_phyaddress);
+ par->v_screen_start_address =
+ (u_char *) ((u_long) g_slcdc_dbuffer_address);
+
+ /* update_lcd ? */
+
+ FUNC_END return 0;
+}
+
+/**
+ *@brief Set current_par by var, also set display data, specially the console
+ * related file operations, then enable the SLCD controller, and set cmap to
+ * hardware.
+ *
+ *@param var Iuput data pointer
+ *@param con Console ID
+ *@param info Frame buffer information
+ *
+ *@return If no error, return 0.
+ *
+ **/
+static int slcdcfb_set_var(struct fb_info *info)
+{
+ struct display *display;
+ int err;
+ struct slcdcfb_par par;
+ struct fb_var_screeninfo *var = &info->var;
+
+ FUNC_START;
+
+ display = &global_disp; /* Default display settings */
+
+ /* Decode var contents into a par structure, adjusting any */
+ /* out of range values. */
+ if ((err = _decode_var(info))) {
+ TRACE("decode var error!");
+ return err;
+ }
+
+ /* Store adjusted par values into var structure */
+ _encode_var(var, &par);
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+ return 0;
+
+ else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
+ ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
+ return -EINVAL;
+
+ display->inverse = 0;
+
+ init_var = *var; /* TODO:gcc support structure copy? */
+
+ FUNC_END;
+ return 0;
+}
+
+/**
+ *@brief Blank the screen, if blank, disable LCD controller, while if no blank
+ * set cmap and enable LCD controller
+ *
+ *@param blank Blank flag
+ *@param info Frame buffer database
+ *
+ *@return VOID
+ */
+static int slcdcfb_blank(int blank, struct fb_info *info)
+{
+
+ if (blank) {
+ slcdc_display_off();
+ } else {
+ slcdc_display_normal();
+ }
+
+ return 0;
+}
+
+/**
+ *@brief Initialize frame buffer. While 16bpp is used to store a 12 bits pixels
+ * packet, it is not a really 16bpp system, maybe in-compatiable with
+ * other system or GUI.There are some field in var which specify
+ * the red/green/blue offset in a 16bit word, just little endian is
+ * concerned
+ *
+ *@return VOID
+ **/
+static void __init _init_fbinfo(void)
+{
+ FUNC_START;
+
+ slcdc_fb_info.node = -1;
+ slcdc_fb_info.flags = 0; /* Low-level driver is not a module */
+ slcdc_fb_info.fbops = &slcdcfb_ops;
+ slcdc_fb_info.monspecs = monspecs;
+
+ strcpy(slcdc_fb_info.fix.id, "SLCDC");
+ /* FIXME... set fix parameters */
+ /*
+ * * setup initial parameters
+ * */
+ memset(&init_var, 0, sizeof(init_var));
+ memset(&current_par, 0, sizeof(current_par));
+
+ init_var.transp.length = 0;
+ init_var.nonstd = 0;
+ init_var.activate = FB_ACTIVATE_NOW;
+ init_var.xoffset = 0;
+ init_var.yoffset = 0;
+ init_var.height = -1;
+ init_var.width = -1;
+ init_var.vmode = FB_VMODE_NONINTERLACED;
+
+ /*xres and yres might be set when loading the module, if this driver is
+ built as module */
+ current_par.max_xres = SLCDC_WIDTH;
+ current_par.max_yres = SLCDC_HIGH;
+
+ current_par.max_bpp = SLCDC_BPP;
+ init_var.red.length = 5;
+ init_var.green.length = 6;
+ init_var.blue.length = 5;
+#ifdef __LITTLE_ENDIAN
+ init_var.red.offset = 11;
+ init_var.green.offset = 5;
+ init_var.blue.offset = 0;
+#endif /* __LITTLE_ENDIAN */
+ init_var.grayscale = 16;
+ init_var.sync = 0;
+ init_var.pixclock = 171521;
+
+ current_par.screen_start_address = NULL;
+ current_par.v_screen_start_address = NULL;
+ current_par.screen_memory_size = MAX_PIXEL_MEM_SIZE;
+ current_par.currcon = -1;
+
+ init_var.xres = current_par.max_xres;
+ init_var.yres = current_par.max_yres;
+ init_var.xres_virtual = init_var.xres;
+ init_var.yres_virtual = init_var.yres;
+ init_var.bits_per_pixel = current_par.max_bpp;
+
+ current_par.xres = init_var.xres;
+ current_par.yres = init_var.yres;
+ current_par.xres_virtual = init_var.xres;
+ current_par.yres_virtual = init_var.yres;
+
+ /* initialize current screen information */
+ memcpy(&slcdc_fb_info.var, &init_var, sizeof(init_var));
+
+ FUNC_END;
+}
+
+/**
+ *@brief slcdc framebuffer open call
+ *
+ * Function Name: slcdcfb_open
+ *
+ * Description : This function is called by the framebuffer when the
+ * application requests.
+ *
+ *@return 0 on success any other value for failure
+ **/
+static int slcdcfb_open(struct fb_info *info, int user)
+{
+ return slcdc_open(NULL, 0);
+}
+
+/**
+ *@brief slcdc framebuffer release call
+ *
+ * Function Name: slcdcfb_release
+ *
+ * Description : This function is called by the framebuffer when the
+ * application closes the link to framebuffer.
+ *
+ *@return 0 on success any other value for failure
+ **/
+static int slcdcfb_release(struct fb_info *info, int user)
+{
+ start_fb_timer_flag = 0;
+ return slcdc_release(NULL, 0);
+}
+
+/*\@}*/
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/mxc/mxcfb.c b/drivers/video/mxc/mxcfb.c
new file mode 100644
index 000000000000..ba631c1b1d7c
--- /dev/null
+++ b/drivers/video/mxc/mxcfb.c
@@ -0,0 +1,1476 @@
+/*
+ * 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 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 <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/mxcfb.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);
+
+ 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_INVERT)
+ 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_ACT_HIGH)
+ 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.hsync_len,
+ fbi->var.upper_margin,
+ fbi->var.vsync_len,
+ fbi->var.lower_margin +
+ fbi->var.vsync_len, sig_cfg) != 0) {
+ dev_err(fbi->device,
+ "mxcfb: Error initializing panel.\n");
+ return -EINVAL;
+ }
+ }
+
+ ipu_sdc_set_window_pos(mxc_fbi->ipu_ch, 0, 0);
+
+ mxc_fbi->cur_ipu_buf = 0;
+ sema_init(&mxc_fbi->flip_sem, 1);
+
+ 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.smem_start +
+ (fbi->fix.line_length * fbi->var.yres),
+ 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_select_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, 0);
+ 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;
+
+ /* Copy nonstd field to/from sync for fbset usage */
+ var->sync |= var->nonstd;
+ var->nonstd |= var->sync;
+
+ 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;
+
+ 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;
+ }
+#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_sdc_set_window_pos(mxc_fbi->ipu_ch,
+ pos.x, pos.y);
+ 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_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_epson.c b/drivers/video/mxc/mxcfb_epson.c
new file mode 100644
index 000000000000..673ca26f4684
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson.c
@@ -0,0 +1,1158 @@
+/*
+ * 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 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 <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/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_qvga.c b/drivers/video/mxc/mxcfb_epson_qvga.c
new file mode 100644
index 000000000000..f7c26fd46558
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_epson_qvga.c
@@ -0,0 +1,1146 @@
+/*
+ * 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 mxcfb_epson_qvga.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 <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/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 12
+#define MXCFB_SCREEN_WIDTH 240
+#define MXCFB_SCREEN_HEIGHT 320
+
+/*!
+ * Enum defining Epson panel commands.
+ */
+enum {
+ DISON = 0x29,
+ DISOFF = 0x28,
+ DISCTL = 0xB0,
+ SD_CSET = 0x2A,
+ SD_PSET = 0x2B,
+ SLPIN = 0x10,
+ SLPOUT = 0x11,
+ DISINOFF = 0x20,
+ RAMWR = 0x2C,
+ VOLCTL = 0xBE,
+};
+
+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 MSB[22:16] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x09);
+ /*WRITE Y START ADDRESS CMND LSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE Y STOP ADDRESS CMND MSB */
+ tempCmd[i++] =
+ ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ (MXCFB_SCREEN_HEIGHT - 1) >> 8);
+ /*WRITE Y STOP ADDRESS CMND LSB */
+ tempCmd[i++] =
+ ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_HEIGHT - 1);
+ /*WRITE X COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, SD_CSET);
+ /*WRITE X ADDRESS CMND MSB[22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP, 0);
+ /*WRITE X ADDRESS CMND LSB[7:0] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_XADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE X STOP ADDRESS CMND MSB */
+ 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 + 11);
+ /*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 disctl_param[] =
+ { 0x01, 0x98, 0x01, 0x48, 0x01, 0x40, 0x04, 0x01, 0x01, 0x41, 0x09,
+ 0x00, 0x00, 0x40, 0x00
+ };
+ uint32_t i;
+
+ gpio_lcd_active();
+ slcd_gpio_config();
+
+ // 16-bit 565 mode
+ cmd_param = 0x03;
+ ipu_adc_write_cmd(disp, CMD, 0xC2, &cmd_param, 1);
+
+ ipu_adc_write_cmd(disp, CMD, DISCTL, disctl_param, 15);
+
+ cmd_param = 0x48;
+ ipu_adc_write_cmd(disp, CMD, 0x36, &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);
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT); i++)
+ 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;
+#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, 200, 17, 90, 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 1
+ 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
+#ifdef PARTIAL_REFRESH
+ ipu_uninit_channel(ADC_SYS2);
+#endif
+ break;
+ case MXCFB_REFRESH_PARTIAL:
+#ifdef PARTIAL_REFRESH
+ 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_modedb.c b/drivers/video/mxc/mxcfb_modedb.c
new file mode 100644
index 000000000000..26622115733a
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_modedb.c
@@ -0,0 +1,63 @@
+/*
+ * 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
+ */
+
+#include <linux/kernel.h>
+#include <asm/arch/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_CLK_INVERT | 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_CLK_INVERT | 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_SYNC_OE_ACT_HIGH,
+ 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/mxcfb_sharp_128x128.c b/drivers/video/mxc/mxcfb_sharp_128x128.c
new file mode 100644
index 000000000000..fd28e9328700
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_sharp_128x128.c
@@ -0,0 +1,1167 @@
+/*
+ * 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 mxcfb_sharp_128x128.c
+ *
+ * @brief MXC Frame buffer driver for Sharp 128x128 panel
+ *
+ * @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 <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/mxcfb.h>
+
+//#define PARTIAL_REFRESH
+#define MXCFB_REFRESH_DEFAULT MXCFB_REFRESH_AUTO
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxcfb_sharp_128"
+
+#define MXCFB_SCREEN_TOP_OFFSET 0
+#define MXCFB_SCREEN_LEFT_OFFSET 2
+#define MXCFB_SCREEN_WIDTH 128
+#define MXCFB_SCREEN_HEIGHT 128
+
+/*!
+ * Enum defining panel commands.
+ */
+enum {
+ SWRESET = 0x01,
+ DISON = 0x29,
+ DISOFF = 0x28,
+ DISCTL = 0xB6,
+ TMPGRD = 0xB7,
+ SD_CSET = 0x2A,
+ SD_PSET = 0x2B,
+ SLPIN = 0x10,
+ SLPOUT = 0x11,
+ DISINOFF = 0x20,
+ RAMWR = 0x2C,
+ MADCTL = 0x36,
+ COLMOD = 0x3A,
+ GCPSET0 = 0xB3,
+ FRSET = 0xB1,
+ VOLCTL = 0xBA,
+ WRCNTR = 0x25,
+ GAMSET = 0x26,
+ DRPOS = 0xBB,
+ PAGEND = 0xBE,
+ MODSEL = 0xC0,
+ COLEND = 0xC3,
+};
+
+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 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.
+ */
+static void set_panel_region(int disp, uint32_t x1, uint32_t x2,
+ uint32_t y1, uint32_t y2)
+{
+ uint32_t param[4];
+
+ param[0] = x1;
+ param[1] = x2;
+ param[2] = y1;
+ param[3] = y2;
+
+ // SD_CSET
+ ipu_adc_write_cmd(disp, CMD, SD_CSET, param, 2);
+ // SD_PSET
+
+ ipu_adc_write_cmd(disp, CMD, SD_PSET, &(param[2]), 2);
+}
+
+/*!
+ * 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 STOP ADDRESS CMND */
+ tempCmd[i++] =
+ ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_HEIGHT - 1);
+ /*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 STOP ADDRESS CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 1, SINGLE_STEP,
+ MXCFB_SCREEN_WIDTH +
+ MXCFB_SCREEN_LEFT_OFFSET - 1);
+ /*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[2];
+ uint32_t disctl_param[] = { 0x0C, 0x48, 0x20 };
+ uint32_t gcpset0_param[] = {
+ 0xAF, 0x92, 0x83, 0x24,
+ 0x52, 0x94, 0x4A, 0x47,
+ 0xAB, 0x75, 0x6A, 0xDF,
+ 0xAB, 0x4B, 0xA5, 0x54,
+ 0x00
+ };
+ uint32_t i;
+
+ (void)gcpset0_param;
+
+ gpio_lcd_active();
+
+ ipu_adc_write_cmd(disp, CMD, SWRESET, 0, 0);
+ msleep(1);
+
+ ipu_adc_write_cmd(disp, CMD, DISCTL, disctl_param, 3);
+
+ // 16-bit 565 mode
+ cmd_param[0] = 0x05;
+ ipu_adc_write_cmd(disp, CMD, COLMOD, cmd_param, 1);
+
+ cmd_param[0] = 0x01;
+ ipu_adc_write_cmd(disp, CMD, TMPGRD, cmd_param, 1);
+
+ cmd_param[0] = 0x2F;
+ cmd_param[1] = 0x02;
+ ipu_adc_write_cmd(disp, CMD, VOLCTL, cmd_param, 2);
+
+ cmd_param[0] = 0x3F;
+ ipu_adc_write_cmd(disp, CMD, WRCNTR, cmd_param, 1);
+
+ cmd_param[0] = 0x00;
+ ipu_adc_write_cmd(disp, CMD, DRPOS, cmd_param, 1);
+
+ cmd_param[0] = 0x00;
+ ipu_adc_write_cmd(disp, CMD, MADCTL, cmd_param, 1);
+
+ cmd_param[0] = 0x05;
+ ipu_adc_write_cmd(disp, CMD, FRSET, cmd_param, 1);
+
+ cmd_param[0] = 0x80;
+ ipu_adc_write_cmd(disp, CMD, PAGEND, cmd_param, 1);
+
+ cmd_param[0] = 0x01;
+ ipu_adc_write_cmd(disp, CMD, GAMSET, cmd_param, 1);
+
+ cmd_param[0] = 0x04;
+ ipu_adc_write_cmd(disp, CMD, MODSEL, 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);
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT); i++) {
+ ipu_adc_write_cmd(disp, DAT, 0xFF, 0, 0);
+ ipu_adc_write_cmd(disp, DAT, 0xFF, 0, 0);
+ }
+
+ msleep(100);
+
+ // Display ON
+ ipu_adc_write_cmd(disp, CMD, DISON, 0, 0);
+
+ 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;
+
+ 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,
+ 8, 0, 0, IPU_ADC_SER_NO_RW
+ };
+ mxc_fbi->disp_num = DISP2;
+
+#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, 110, 25, 80, 0, 0);
+
+ // Needed to turn on ADC clock for panel init
+ memset(&params, 0, sizeof(params));
+ params.adc_sys2.disp = mxc_fbi->disp_num;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = MXCFB_SCREEN_LEFT_OFFSET;
+ params.adc_sys2.out_top = MXCFB_SCREEN_TOP_OFFSET;
+ ipu_init_channel(ADC_SYS2, &params);
+
+ _init_panel(mxc_fbi->disp_num);
+ init_channel_template(mxc_fbi->disp_num);
+}
+
+static int _mxcfb_set_refresh_mode(struct fb_info *fbi, int mode,
+ struct mxcfb_rect *update_region)
+{
+ unsigned long start_addr;
+ int ret_mode;
+ 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_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+ ipu_disable_channel(ADC_SYS2, true);
+#ifdef PARTIAL_REFRESH
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0);
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+
+ ipu_disable_channel(ADC_SYS1, true);
+ ipu_clear_irq(IPU_IRQ_ADC_SYS1_EOF);
+ 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 1
+ 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
+#ifdef PARTIAL_REFRESH
+ ipu_uninit_channel(ADC_SYS2);
+#endif
+ break;
+ case MXCFB_REFRESH_PARTIAL:
+#ifdef PARTIAL_REFRESH
+ params.adc_sys2.disp = mxc_fbi->disp_num;
+ 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_sys2.disp = mxc_fbi->disp_num;
+ params.adc_sys2.ch_mode = WriteTemplateNonSeq;
+ params.adc_sys2.out_left = MXCFB_SCREEN_LEFT_OFFSET +
+ update_region->left;
+ params.adc_sys2.out_top = MXCFB_SCREEN_TOP_OFFSET +
+ update_region->top;
+ ipu_init_channel(ADC_SYS2, &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_SYS2, 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_SYS2);
+ ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 0);
+
+ if (ipu_adc_set_update_mode
+ (ADC_SYS2, IPU_ADC_AUTO_REFRESH, 15,
+ 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);
+
+ mxcfbi->disp_num = DISP2;
+ 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);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Sharp 128x128 framebuffer driver");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/video/mxc/mxcfb_toshiba_qvga.c b/drivers/video/mxc/mxcfb_toshiba_qvga.c
new file mode 100644
index 000000000000..a8e56bc6ed03
--- /dev/null
+++ b/drivers/video/mxc/mxcfb_toshiba_qvga.c
@@ -0,0 +1,1202 @@
+/*
+ * 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 mxcfb_toshiba_qvga.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 <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/uaccess.h>
+#include <asm/arch/ipu.h>
+#include <asm/arch/mxcfb.h>
+
+#define PARTIAL_REFRESH
+#define MXCFB_REFRESH_DEFAULT MXCFB_REFRESH_PARTIAL
+/*
+ * Driver name
+ */
+#define MXCFB_NAME "mxcfb_toshiba_qvga"
+
+#define MXCFB_SCREEN_TOP_OFFSET 0
+#define MXCFB_SCREEN_LEFT_OFFSET 0
+#define MXCFB_SCREEN_WIDTH 240
+#define MXCFB_SCREEN_HEIGHT 320
+
+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;
+}
+
+/*!
+ * 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, 0x07);
+ /*WRITE Y START ADDRESS CMND [22:8] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_YADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE X COORDINATE CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, 0x06);
+ /*WRITE X START ADDRESS CMND [7:0] */
+ tempCmd[i++] = ipu_adc_template_gen(WR_XADDR, 1, SINGLE_STEP, 0x01);
+ /*WRITE RAM CMND */
+ tempCmd[i++] = ipu_adc_template_gen(WR_CMND, 0, SINGLE_STEP, 0x0E);
+ /*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;
+
+ slcd_gpio_config();
+
+ // Reset
+ cmd_param = 0x01;
+ ipu_adc_write_cmd(disp, CMD, 0x03, &cmd_param, 1);
+
+ // Turn on oscillator
+ cmd_param = 0x01;
+ ipu_adc_write_cmd(disp, CMD, 0x3A, &cmd_param, 1);
+
+ cmd_param = 0x02;
+ ipu_adc_write_cmd(disp, CMD, 0x32, &cmd_param, 1);
+
+ cmd_param = 0x01;
+ ipu_adc_write_cmd(disp, CMD, 0x33, &cmd_param, 1);
+
+ cmd_param = 0x00;
+ ipu_adc_write_cmd(disp, CMD, 0x37, &cmd_param, 1);
+
+ cmd_param = 0x0FFF;
+ ipu_adc_write_cmd(disp, CMD, 0x77, &cmd_param, 1);
+
+ cmd_param = 0x01;
+ ipu_adc_write_cmd(disp, CMD, 0x72, &cmd_param, 1);
+
+ cmd_param = 0x1C3B;
+ ipu_adc_write_cmd(disp, CMD, 0x1C, &cmd_param, 1);
+
+ cmd_param = 0x21;
+ ipu_adc_write_cmd(disp, CMD, 0x52, &cmd_param, 1);
+
+ cmd_param = 0x11;
+ ipu_adc_write_cmd(disp, CMD, 0x53, &cmd_param, 1);
+
+ cmd_param = 0x79;
+ ipu_adc_write_cmd(disp, CMD, 0x24, &cmd_param, 1);
+
+ cmd_param = 0x79;
+ ipu_adc_write_cmd(disp, CMD, 0x25, &cmd_param, 1);
+
+ cmd_param = 0x10;
+ ipu_adc_write_cmd(disp, CMD, 0x26, &cmd_param, 1);
+
+ cmd_param = 0x10;
+ ipu_adc_write_cmd(disp, CMD, 0x27, &cmd_param, 1);
+
+ cmd_param = 0x28;
+ ipu_adc_write_cmd(disp, CMD, 0x61, &cmd_param, 1);
+
+ cmd_param = 0x1A;
+ ipu_adc_write_cmd(disp, CMD, 0x62, &cmd_param, 1);
+
+ cmd_param = 0x1E;
+ ipu_adc_write_cmd(disp, CMD, 0x63, &cmd_param, 1);
+
+ cmd_param = 0x21;
+ ipu_adc_write_cmd(disp, CMD, 0x64, &cmd_param, 1);
+
+ cmd_param = 0x1B;
+ ipu_adc_write_cmd(disp, CMD, 0x65, &cmd_param, 1);
+
+ cmd_param = 0x29;
+ ipu_adc_write_cmd(disp, CMD, 0x66, &cmd_param, 1);
+
+ cmd_param = 0x205;
+ ipu_adc_write_cmd(disp, CMD, 0x4D, &cmd_param, 1);
+
+ cmd_param = 0x01;
+ ipu_adc_write_cmd(disp, CMD, 0x4E, &cmd_param, 1);
+
+ cmd_param = 0x104;
+ ipu_adc_write_cmd(disp, CMD, 0x4F, &cmd_param, 1);
+
+ cmd_param = 0x2F;
+ ipu_adc_write_cmd(disp, CMD, 0x2E, &cmd_param, 1);
+
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x29, &cmd_param, 1);
+
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x2A, &cmd_param, 1);
+
+ cmd_param = 0xEF;
+ ipu_adc_write_cmd(disp, CMD, 0x2B, &cmd_param, 1);
+
+ cmd_param = 0x13F;
+ ipu_adc_write_cmd(disp, CMD, 0x2C, &cmd_param, 1);
+
+ /* Window area setting */
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x08, &cmd_param, 1);
+ cmd_param = 0xEF;
+ ipu_adc_write_cmd(disp, CMD, 0x09, &cmd_param, 1);
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x0A, &cmd_param, 1);
+ cmd_param = 0x13F;
+ ipu_adc_write_cmd(disp, CMD, 0x0B, &cmd_param, 1);
+
+ /* Window mode setting */
+ cmd_param = 0x00;
+ ipu_adc_write_cmd(disp, CMD, 0x05, &cmd_param, 1);
+
+ /* Ram address setting */
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x06, &cmd_param, 1);
+ cmd_param = 0x0;
+ ipu_adc_write_cmd(disp, CMD, 0x07, &cmd_param, 1);
+
+ /* Initialize RAM */
+ ipu_adc_write_cmd(disp, CMD, 0x0E, 0, 0);
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT / 2); i++)
+ ipu_adc_write_cmd(disp, DAT, 0x3FFFF, 0, 0);
+ for (i = 0; i < (MXCFB_SCREEN_WIDTH * MXCFB_SCREEN_HEIGHT / 2); i++)
+ ipu_adc_write_cmd(disp, DAT, 0, 0, 0);
+
+ cmd_param = 0x1F6A;
+ ipu_adc_write_cmd(disp, CMD, 0x18, &cmd_param, 1);
+
+ cmd_param = 0x00A2;
+ ipu_adc_write_cmd(disp, CMD, 0x1A, &cmd_param, 1);
+
+ cmd_param = 0x0028;
+ ipu_adc_write_cmd(disp, CMD, 0x1B, &cmd_param, 1);
+
+ cmd_param = 0x1C3B;
+ ipu_adc_write_cmd(disp, CMD, 0x1C, &cmd_param, 1);
+
+ cmd_param = 0x0075;
+ ipu_adc_write_cmd(disp, CMD, 0x1D, &cmd_param, 1);
+
+ cmd_param = 0x003D;
+ ipu_adc_write_cmd(disp, CMD, 0x1F, &cmd_param, 1);
+
+ cmd_param = 0x0080;
+ ipu_adc_write_cmd(disp, CMD, 0x20, &cmd_param, 1);
+
+ /* DC/DC on */
+ cmd_param = 0x1F6B;
+ ipu_adc_write_cmd(disp, CMD, 0x18, &cmd_param, 1);
+ msleep(100);
+
+ /* VCOM on */
+ cmd_param = 0x0021;
+ ipu_adc_write_cmd(disp, CMD, 0x1E, &cmd_param, 1);
+
+ /* GOE1,GOE2 setting (Gate output enable) */
+ cmd_param = 0x0001;
+ ipu_adc_write_cmd(disp, CMD, 0x3B, &cmd_param, 1);
+
+ cmd_param = 0x00;
+ ipu_adc_write_cmd(disp, CMD, 0x2, &cmd_param, 1);
+ /* Display on */
+ cmd_param = 0x00;
+ ipu_adc_write_cmd(disp, CMD, 0x0, &cmd_param, 1);
+
+ msleep(10);
+
+ pr_debug("initialized panel\n");
+}
+
+#ifdef PARTIAL_REFRESH
+static void mxcfb_update_region(struct fb_info *fbi, uint32_t statl,
+ uint32_t stath)
+{
+ ipu_channel_params_t params;
+ struct mxcfb_info *mxc_fbi = fbi->par;
+ uint32_t 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;
+
+ lsb = ffs(statl);
+ if (lsb) {
+ lsb--;
+ } else {
+ lsb = ffs(stath);
+ lsb += 32 - 1;
+ }
+ msb = fls(stath);
+ if (msb) {
+ msb += 32;
+ } else {
+ msb = fls(statl);
+ }
+
+ 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);
+}
+
+static irqreturn_t mxcfb_sys2_eof_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ uint32_t stat[2];
+
+ ipu_adc_get_snooping_status(&stat[0], &stat[1]);
+ if (stat[0] || stat[1]) {
+ mxcfb_update_region(fbi, stat[0], stat[1]);
+ ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxcfb_sys1_eof_irq_handler(int irq, void *dev_id)
+{
+ struct fb_info *fbi = dev_id;
+ uint32_t stat[2];
+
+ ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF);
+ ipu_disable_channel(ADC_SYS1, false);
+
+ ipu_adc_get_snooping_status(&stat[0], &stat[1]);
+ if (stat[0] || stat[1]) {
+ mxcfb_update_region(fbi, stat[0], stat[1]);
+ } else {
+ 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;
+ 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_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,
+ 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;
+#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, 90, 10, 60, 0, 0);
+
+ _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");
+
+ ipu_uninit_channel(ADC_SYS1);
+#ifdef PARTIAL_REFRESH
+ ipu_uninit_channel(ADC_SYS2);
+#endif
+ break;
+ case MXCFB_REFRESH_PARTIAL:
+#ifdef PARTIAL_REFRESH
+ 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)
+{
+ uint32_t cmd_param;
+ 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 */
+ cmd_param = 0x08;
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, 0x0, &cmd_param, 1);
+
+ 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)
+{
+ uint32_t cmd_param;
+ 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
+ cmd_param = 0x00;
+ ipu_adc_write_cmd(mxc_fbi->disp_num, CMD, 0x0, &cmd_param, 1);
+ 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 Toshiba QVGA framebuffer driver");
+MODULE_SUPPORTED_DEVICE("fb");
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 8236d447adf5..9cc7a6b71530 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
diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile
index 11551b328186..55b169a6c9a1 100644
--- a/drivers/w1/masters/Makefile
+++ b/drivers/w1/masters/Makefile
@@ -5,4 +5,6 @@
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
diff --git a/drivers/w1/masters/mxc_w1.c b/drivers/w1/masters/mxc_w1.c
new file mode 100644
index 000000000000..8164951fb39a
--- /dev/null
+++ b/drivers/w1/masters/mxc_w1.c
@@ -0,0 +1,432 @@
+/*
+ * 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 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 <asm/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, 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(data, 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;
+ 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);
+ 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);
+ return -1;
+ }
+ }
+ err = w1_add_master_device(dev->bus_master);
+ if (err)
+ goto err_out_free_device;
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+ 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);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ if (dev->found) {
+ w1_remove_master_device(dev->bus_master);
+ }
+ 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 3df29a122f84..b474c333325c 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -16,6 +16,23 @@ 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_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
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index a8eb7524df1d..92e7768b60e3 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
+obj-$(CONFIG_W1_SLAVE_DS2751) += w1_ds2751.o
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 ef1e1dafa19a..f77988ae5614 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -32,6 +32,7 @@
#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_FAMILY_DS2760 0x30
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 52dff40ec192..c47f7f71a973 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -189,6 +189,18 @@ 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 IOP_WATCHDOG
tristate "IOP Watchdog"
depends on PLAT_IOP
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 87483cc63252..379898b7617d 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -36,6 +36,7 @@ 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_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o